Calcule una propina usando la menor cantidad de monedas

23

La mayoría de las aplicaciones de calculadora de propinas simplemente toman un porcentaje fijo del precio de la comida. Entonces, por ejemplo, si su comida es de $ 23.45, puede dejar una propina del 15% = $ 3.52, o una propina más generosa del 20% = $ 4.69.

Lo suficientemente conveniente para los usuarios de tarjetas de crédito. Pero no es así si prefiere dejar propinas en efectivo, en cuyo caso estas cantidades de centavos extraños se interponen en el camino. Entonces modifiquemos la idea para que sea más conveniente para los usuarios de efectivo.

Su tarea

Escriba, en la menor cantidad de bytes posible, un programa o función que tome como entrada:

  • Precio de la comida
  • Porcentaje mínimo de propina
  • Porcentaje máximo de propina

Y envíe cualquier cantidad de propina dentro del rango [price * min_percentage / 100, price * max_percentage / 100] que minimiza el número de billetes / billetes y monedas requeridos.

Suponga las denominaciones monetarias estadounidenses de 1 ¢, 5 ¢, 10 ¢, 25 ¢, $ 1, $ 5, $ 10, $ 20, $ 50 y $ 100.

Ejemplo

Aquí hay un programa de ejemplo sin golf en 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)))

Algunas entradas y salidas de muestra:

~$ 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
fuente
8
Si no está utilizando una tarjeta de crédito, está pagando en efectivo, ¿verdad? ¿No sería el total de cheque + propina la cantidad relevante entonces, no solo la propina?
Sparr
44
a program that takes as input (stdin, command-line arguments, or GUI input box, whichever is most convenient in your language)¿Se pretende anular nuestros valores predeterminados para entradas y salidas? Es decir, ¿se permitiría, por ejemplo, una función que toma tres números y devuelve el resultado?
Laikoni
3
¿Estoy en lo cierto al decir eso 3.51y 3.75también son salidas válidas para el caso de prueba 23.45 15 17? Usan la misma cantidad de monedas y también están dentro del rango.
Kevin Cruijssen
3
@Sparr Incluso a las personas que pagan la factura con tarjeta les gusta dejar una propina en efectivo; Hay varias razones para esto, así que no las repetiré aquí.
Neil
3
@Laikoni: he editado los requisitos para usar el "programa o función" predeterminado en el sitio, por lo que acepto retroactivamente las respuestas de solo función existentes.
dan04

Respuestas:

3

Carbón , 60 bytes

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

Pruébalo en línea! Toma datos como decimales. El enlace es a la versión detallada del código. Explicación:

Nθ

Ingrese la factura.

≔×θNη≔×θNζ

Ingrese las fracciones decimales de punta y calcule la punta mínima y máxima.

≔⁰θ

Comience con punta cero.

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

La cadena SEXY se expande y 10050.20.10.5.01.0.250.1.05.01se divide en grupos de tres caracteres y se lanza para flotar.

W‹θη≧⁺ιθ

Agregue tantas de la denominación actual como sea necesario para alcanzar la punta mínima.

¿›θζ≧⁻ιθ»

Elimine una denominación si se ha excedido la propina máxima.

﹪%.2fθ

Formatee la punta para su visualización.

Neil
fuente
1
No creo que el formato fuera un requisito (sino algo que hace el código de ejemplo).
Jonathan Allan
@JonathanAllan Bueno, en ese caso, podría ahorrar 4 bytes utilizando en lugar de ﹪%.2f.
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

Pruébalo en línea!

¿Cómo?

Calculamos recursivamente una suma de valores de billetes / monedas hasta que se encuentre dentro del rango aceptable, siempre intentando primero el valor más 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
fuente
4

Python 3.x: 266185 bytes

Una modificación directa a mi programa de ejemplo en la pregunta. Tenga en cuenta que la salida ya no está formateada para requerir 2 lugares decimales.

Editar: Gracias a Jo King por hacerlo más pequeño.

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
fuente
1
Un poco de golf rápido para llegar a 185 bytes
Jo King
4

Java 10, 186185 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 los porcentajes mínimo y máximo como /100decimales (es decir, 15%como 0.15).

-1 byte para solucionar el problema 3.51como salida potencial y jugando al golf para corregir los errores de redondeo en 1 byte al mismo tiempo.

Pruébalo en línea.

Explicación:

(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
fuente
No creo que esto sea técnicamente válido en este momento ya que la pregunta especifica un programa, pero el OP no lo ha aclarado.
Οurous
1
OP ha aclarado que ahora se permiten funciones, por lo que no debe preocuparse por duplicar el tamaño.
Horrible
3

Limpio , 207156 bytes

Cambiar a una función ahorró 51 bytes, como era de esperar.

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

Pruébalo en línea!

Οurous
fuente
2
OP aclaró que las funciones ahora están permitidas.
Laikoni
@Laikoni Gracias por avisarme :) Ahorra muchos bytes: ¡los programas completos son caros en Clean!
Agradable
2

Python ( 264 222 bytes)

Un poco más golfizado.

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]

Pruébalo en línea!

Algodón Zachary
fuente
2

Perl 6 , 93 92 89 bytes

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

Pruébalo en línea!

Bloque de código anónimo que toma tres argumentos (precio, porcentaje mínimo y porcentaje máximo) y devuelve la propina.

Jo King
fuente
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}

Pruébalo en línea!

JohnWells
fuente
0

Jalea ,  33  32 bytes

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

Un enlace monádico que acepta una lista [cost in cents, [minimum ratio, maximum ratio]]que produce una cantidad de propina en centavos.

Pruébalo en línea!

¿Cómo?

La primera línea es un enlace auxiliar que produce la cantidad dada menos la nota / moneda de mayor denominación:

“ñṇ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)

El número de llamadas requeridas para llegar a cero se usa para ordenar el rango de cantidades de propinas, y luego se obtiene el extremo izquierdo:

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
fuente