Calcular uma dica usando o menor número de moedas

23

A maioria dos aplicativos de calculadora de gorjetas simplesmente cobra uma porcentagem fixa do preço da refeição. Assim, por exemplo, se a sua refeição for $ 23,45, você pode deixar uma gorjeta de 15% = $ 3,52 ou uma gorjeta mais generosa de 20% = $ 4,69.

Conveniente o suficiente para usuários de cartão de crédito. Mas não é assim, se você preferir deixar gorjetas em dinheiro, caso em que esses montantes excêntricos ficarão no caminho. Então, vamos modificar a ideia para ser mais conveniente para usuários de dinheiro.

Sua tarefa

Escreva, no menor número possível de bytes, um programa ou função que use como entrada:

  • Preço da refeição
  • Percentual mínimo de gorjeta
  • Porcentagem máxima da ponta

E produza qualquer valor de gorjeta dentro do intervalo [price * min_percentage / 100, price * max_percentage / 100] que minimiza o número de notas / notas e moedas necessárias.

Suponha as denominações monetárias dos EUA de 1, 5, 10, 25, US $ 1, US $ 5, US $ 10, US $ 20, US $ 50 e US $ 100.

Exemplo

Aqui está um programa de exemplo sem golfe no Python:

import math
import sys

# Do the math in cents so we can use integer arithmetic
DENOMINATIONS = [10000, 5000, 2000, 1000, 500, 100, 25, 10, 5, 1]

def count_bills_and_coins(amount_cents):
    # Use the Greedy method, which works on this set of denominations.
    result = 0
    for denomination in DENOMINATIONS:
        num_coins, amount_cents = divmod(amount_cents, denomination)
        result += num_coins
    return result

def optimize_tip(meal_price, min_tip_percent, max_tip_percent):
    min_tip_cents = int(math.ceil(meal_price * min_tip_percent))
    max_tip_cents = int(math.floor(meal_price * max_tip_percent))
    best_tip_cents = None
    best_coins = float('inf')
    for tip_cents in range(min_tip_cents, max_tip_cents + 1):
        num_coins = count_bills_and_coins(tip_cents)
        if num_coins < best_coins:
            best_tip_cents = tip_cents
            best_coins = num_coins
    return best_tip_cents / 100.0

# Get inputs from command-line
meal_price = float(sys.argv[1])
min_tip_percent = float(sys.argv[2])
max_tip_percent = float(sys.argv[3])
print('{:.2f}'.format(optimize_tip(meal_price, min_tip_percent, max_tip_percent)))

Alguma amostra de entrada e saída:

~$ python tipcalc.py 23.45 15 20
4.00
~$ python tipcalc.py 23.45 15 17
3.55
~$ python tipcalc.py 59.99 15 25
10.00
~$ python tipcalc.py 8.00 13 20
1.05
dan04
fonte
8
Se você não estiver usando um cartão de crédito, estará pagando em dinheiro, certo? O total de cheque + gorjeta não seria o valor relevante, não apenas a gorjeta?
Sparr
4
a program that takes as input (stdin, command-line arguments, or GUI input box, whichever is most convenient in your language)Isso pretende substituir nossos padrões de entradas e saídas? Ou seja, por exemplo, seria permitida uma função que pega três números e retorna o resultado?
Laikoni 26/07/19
3
Estou correto ao dizer isso 3.51e 3.75também são saídas válidas para o caso de teste 23.45 15 17? Eles usam a mesma quantidade de moedas e também estão dentro do intervalo.
Kevin Cruijssen
3
@Sparr Mesmo as pessoas que pagam a conta com cartão gostam de deixar uma gorjeta em dinheiro; existem várias razões para isso, então não vou repeti-las aqui.
305 Neil
3
@Laikoni: Eu editei os requisitos para usar o "programa ou função" padrão do site e, portanto, estou aceitando retroativamente as respostas somente para funções existentes.
dan04

Respostas:

3

Carvão , 60 bytes

Nθ≔×θNη≔×θNζ≔⁰θFI⪪”;‴üφ↷Σ↗SEX&¿h'⊟”³«W‹θη≧⁺ιθ¿›θζ≧⁻ιθ»﹪%.2fθ

Experimente online! Aceita entrada como decimais. Link é a versão detalhada do código. Explicação:

Nθ

Insira a conta.

≔×θNη≔×θNζ

Insira as frações decimais da ponta e calcule a ponta mínima e máxima.

≔⁰θ

Comece com ponta zero.

FI⪪”;‴üφ↷Σ↗SEX&¿h'⊟”³«

A sequência SEXy se expande para a 10050.20.10.5.01.0.250.1.05.01qual é dividida em grupos de três caracteres e convertida para flutuar.

W‹θη≧⁺ιθ

Adicione a denominação atual necessária para atingir a ponta mínima.

¿›θζ≧⁻ιθ»

Remova uma denominação se a ponta máxima tiver sido excedida.

﹪%.2fθ

Formate a dica para exibição.

Neil
fonte
1
Eu não acho que a formatação era um requisito (e sim algo que o código de exemplo faz).
Jonathan Allan
@ JonathanAllan Bem, nesse caso, você pode salvar 4 bytes usando em vez de ﹪%.2f.
284 Neil
6

JavaScript (ES6), 93 bytes

(x,m,M)=>(g=(t,c=1e4)=>t>x*M?0:t<x*m?[...'1343397439'].some(d=>g(t+(c/=-~d/2)))*r:r=t)(0)/100

Experimente online!

Quão?

Calculamos recursivamente uma soma dos valores da nota / moeda até que ela caia dentro do intervalo aceitável, sempre tentando primeiro o valor mais alto.

{b0,,bn}

  • b0bn{b0,,bk1,x}xbk0k<n
  • 0k<nxbn{b0,,bk1,bkx,bk+1,,bn}{b0,,bn1}
  • 0<x<bn{b0,,bn1,x}

cn

{c0=10000cn+1=cn(dn+1)/2

(d0,,d9)=(1,3,4,3,3,9,7,4,3,9)

 n | c(n)  | d(n) | k = (d(n)+1)/2 | c(n+1) = c(n)/k
---+-------+------+----------------+-----------------
 0 | 10000 |   1  | (1+1)/2 = 1    |      10000
 1 | 10000 |   3  | (3+1)/2 = 2    |       5000
 2 |  5000 |   4  | (4+1)/2 = 2.5  |       2000
 3 |  2000 |   3  | (3+1)/2 = 2    |       1000
 4 |  1000 |   3  | (3+1)/2 = 2    |        500
 5 |   500 |   9  | (9+1)/2 = 5    |        100
 6 |   100 |   7  | (7+1)/2 = 4    |         25
 7 |    25 |   4  | (4+1)/2 = 2.5  |         10
 8 |    10 |   3  | (3+1)/2 = 2    |          5
 9 |     5 |   9  | (9+1)/2 = 5    |          1
Arnauld
fonte
4

Python 3.x: 266 185 bytes

Uma modificação direta no meu programa de exemplo na questão. Observe que a saída não é mais formatada para exigir 2 casas decimais.

Edit: Obrigado a Jo King por torná-lo menor.

import sys
p,m,M,T=*map(float,sys.argv[1:]),0
C=p*M
for t in range(-int(-p*m),int(p*M)+1):
 n,a=0,t
 for d in 1e4,5e3,2e3,1e3,500,100,25,10,5,1:n+=a//d;a%=d
 if n<C:T,C=t,n
print(T/100)
dan04
fonte
1
Alguns golfe rápido para obtê-lo para 185 bytes
Jo rei
4

Java 10, 186 185 bytes

(p,m,M)->{double r=0,t,Q=99,q;for(m*=p+.02;m<M*p;m+=.01){q=0;t=m;for(var c:new double[]{100,50,20,10,5,1,.25,.1,.05,.01})for(;t>=c;t-=c)q++;if(q<Q){Q=q;r=m;}}return"".format("%.2f",r);}

Toma as porcentagens mínima e máxima como /100decimais (ou seja, 15%como 0.15).

-1 byte para corrigir o problema com 3.51saída potencial e jogar no golfe para corrigir erros de arredondamento em 1 byte ao mesmo tempo.

Experimente online.

Explicação:

(p,m,M)->{                // Method with three double parameters and String return-type
  double r=0,             //  Result-double, starting at 0
         t,               //  Temp-double
         Q=99,            //  Min amount of coins, starting at 99
         q;               //  Temp-double for the amount of coins
  for(m*=p-.02;m<M*p;     //  Loop in the range [`m*p-0.02`, `M*p`]
           m+=.01){       //  in steps of 0.01 (1 cent) per iteration
                          //  (the -0.02 (minus 2 cents) is to fix rounding errors)
    q=0;                  //   Reset `q` to 0
    t=m;                  //   Reset `t` to the current iteration `m`
    for(var c:new double[]{100,50,20,10,5,1,.25,.1,.05,.01})
                          //   Loop over the coins (largest to smallest)
      for(;t>=c;          //    As long as `t` is larger than or equal to the current coin
          t-=c)           //     Remove the coin from the value `t`
          q++;            //     And increase the quantity-counter by 1
      if(q<Q){            //   If the quantity-counter is smaller than the current smallest
        Q=q;              //    Replace the smallest with the current
        r=m;}}            //    And replace the result with the current `m`
  return"".format("%.2f",r)l;}
                          //  Return the result with 2 decimal places
Kevin Cruijssen
fonte
Eu não acho que isso seja tecnicamente válido no momento, já que a pergunta especifica um programa, mas o OP não esclareceu.
Οurous
1
O OP esclareceu funções agora são permitidas, portanto você não precisa se preocupar em dobrar o tamanho.
Οurous
3

Limpo , 207 156 bytes

Trocar para uma função economizou 51 bytes, sem surpresa.

import StdEnv
d=[10000,2000,1000,500,100,25,10,5,1]
$n u l=snd(hd(sort[(sum[foldl(rem)m(d%(0,i))/k\\k<-d&i<-[-1..]],toReal m)\\m<-map toInt[n*u..n*l]]))/1E2

Experimente online!

Furioso
fonte
2
O OP esclareceu que as funções agora são permitidas.
Laikoni 27/07
@Laikoni Obrigado por me informar :) Economiza muitos bytes - os programas completos são caros no Clean!
Οurous
2

Python ( 264 222 bytes)

Um pouco mais de golfe.

m=[.01,.05,.1,.25,.5,1,5,10,20,50,100]
def f(a,i,j):
 t,u=9**9,a*j
 def g(s,d,c):
  nonlocal t
  if(a*i<s<u)+(c>t):t=min(c,t);return c,s
  return(t+1,s)if(s>u)+(d>9)else min(g(s+m[d],d,c+1),g(s,d+1,c))
 return g(0,0,0)[1]

Experimente Online!

Zachary Cotton
fonte
2

Perl 6 , 93 92 89 bytes

{.01*($^a*$^b+|0...$a*$^c).min:{$!=$_;sum '✐ᎈߐϨǴd
'.ords>>.&{($!%=$_)xx$!/$_}}}

Experimente online!

Bloco de código anônimo que usa três argumentos (preço, porcentagem mínima e porcentagem máxima) e retorna a dica.

Brincadeira
fonte
1

Wolfram Language (Mathematica) , 105 bytes

Isso fornecerá todas as soluções com contagem mínima de moedas.

MinimalBy[NumberDecompose[#,d=100{100,50,20,10,5,1,.25,.10,.05,.01}]&/@Range[Ceiling[#2#],#3#],Tr].d/100&

Experimente online!

Kelly Lowder
fonte
0

Kotlin , 215 bytes

{p:Double,l:Int,h:Int->val d=listOf(10000,5000,2000,1000,500,100,25,10,5,1)
var m=Int.MAX_VALUE
var r=0.0
(Math.ceil(p*l).toInt()..(p*h).toInt()).map{var a=it
var c=0
d.map{c+=a/it
a%=it}
if(c<m){m=c
r=it/100.0}}
r}

Experimente online!

JohnWells
fonte
0

Geléia ,  33  32 bytes

“ñṇzi;’b⁴×H¥\ɓ_>Ƈ-Ṫ
PĊ1¦r/ÇƬL$ÞḢ

Um link monádico que aceita uma lista [cost in cents, [minimum ratio, maximum ratio]]que gera uma quantia de gorjeta em centavos.

Experimente online!

Quão?

A primeira linha é um elo auxiliar que gera a quantia dada menos a maior nota / moeda denominada:

“ñṇzi;’b⁴×H¥\ɓ_>Ƈ-Ṫ - Link 1, get next lower amount: integer, V
“ñṇzi;’             - base 250 number = 112835839060
       b⁴           - to base 16 = [1,10,4,5,8,10,4,4,5,4]
            \       - cumulative reduce with:       e.g.: 1,10   5,4   10,5   25,8
           ¥        -   last two links as a dyad:
         ×          -     multiply                        10     20    50     200
          H         -     halve                            5     10    25     100
                    - ...yielding: [1,5,10,25,100,500,1000,2000,5000,10000]
             ɓ      - start a new dyadic link with swapped arguments
              _     - subtract (vectorises) ...i.e. [V-1,V-5,V-10,...]
                Ƈ   - filter keep those which satisfy:
                 -  -   literal -1
               >    -   greater than? (i.e. if V-X > -1)
                  Ṫ - tail (tailing an empty list yields 0)

O número de chamadas necessárias para atingir zero é usado para classificar o intervalo de valores das gorjetas e, em seguida, o mais à esquerda é gerado:

PĊ1¦r/ÇƬL$ÞḢ - Main Link: [cost, [min, max]]
P            - product = [cost*min, cost*max]
   ¦         - sparse application...
  1          - ...to indices: 1
 Ċ           - ...what: ceiling   -> [ceil(cost*min), cost*max]
     /       - reduce by:
    r        -   inclusive range (implicit floor of arguments)
          Þ  - sort by:
         $   -   last two links as a monad:
       Ƭ     -     repeat collecting results until a fixed point is reached:
      Ç      -       last link (1) as a monad  (e.g. 32 -> [32,7,2,1,0])
        L    -     length (i.e. coins/notes required + 1)
           Ḣ - head
Jonathan Allan
fonte