Calculez un pourboire en utilisant le plus petit nombre de pièces

23

La plupart des applications de calcul de pourboire prennent simplement un pourcentage fixe du prix du repas. Ainsi, par exemple, si votre repas est de 23,45 $, vous pouvez laisser un pourboire de 15% = 3,52 $, ou un pourboire plus généreux de 20% = 4,69 $.

Assez pratique pour les utilisateurs de cartes de crédit. Mais ce n'est pas le cas si vous préférez laisser des pourboires en espèces, auquel cas ces montants excentriques vous gênent. Modifions donc l'idée pour qu'elle soit plus pratique pour les utilisateurs de cash.

Votre affectation

Écrivez, en un minimum d'octets, un programme ou une fonction qui prend en entrée:

  • Prix ​​du repas
  • Pourcentage minimum de pourboire
  • Pourcentage maximum de pourboire

Et produisez n'importe quel montant de pourboire dans la plage [prix * min_percentage / 100, prix * max_percentage / 100] qui minimise le nombre de billets / billets et pièces requis.

Supposons les coupures monétaires américaines de 1 ¢, 5 ¢, 10 ¢, 25 ¢, 1 $, 5 $, 10 $, 20 $, 50 $ et 100 $.

Exemple

Voici un exemple de programme non 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)))

Quelques exemples d'entrées et de sorties:

~$ 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
la source
8
Si vous n'utilisez pas de carte de crédit, vous payez en espèces, non? Le total du chèque + pourboire ne serait-il pas le montant pertinent alors, pas seulement le pourboire?
Sparr
4
a program that takes as input (stdin, command-line arguments, or GUI input box, whichever is most convenient in your language)Est-ce destiné à remplacer nos valeurs par défaut pour les entrées et les sorties? C'est-à-dire, par exemple, une fonction qui prend trois nombres et renvoie le résultat serait-elle autorisée?
Laikoni
3
Ai-je raison de dire cela 3.51et 3.75sont également des sorties valides pour le cas de test 23.45 15 17? Ils utilisent la même quantité de pièces et sont également à l'intérieur de la gamme.
Kevin Cruijssen
3
@Sparr Même les personnes qui paient la facture par carte aiment laisser un pourboire en espèces; il y a plusieurs raisons à cela, donc je ne les répéterai pas ici.
Neil
3
@Laikoni: J'ai modifié les conditions requises pour utiliser le "programme ou la fonction" par défaut du site, et j'accepte donc rétroactivement les réponses existantes concernant uniquement les fonctions.
dan04

Réponses:

3

Charbon de bois , 60 octets

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

Essayez-le en ligne! Prend l'entrée en décimales. Le lien est vers la version détaillée du code. Explication:

Nθ

Saisissez la facture.

≔×θNη≔×θNζ

Saisissez les fractions décimales de la pointe et calculez la pointe minimale et maximale.

≔⁰θ

Commencez avec zéro pourboire.

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

La chaîne SEXy se développe et 10050.20.10.5.01.0.250.1.05.01est divisée en groupes de trois caractères et convertie en flottant.

W‹θη≧⁺ιθ

Ajoutez autant de dénominations actuelles que nécessaire pour atteindre la pointe minimale.

¿›θζ≧⁻ιθ»

Retirez une dénomination si la pointe maximale a été dépassée.

﹪%.2fθ

Formatez la pointe pour l'affichage.

Neil
la source
1
Je ne pense pas que le formatage était une exigence (plutôt juste quelque chose que fait l'exemple de code).
Jonathan Allan
@JonathanAllan Eh bien, dans ce cas, vous pourriez économiser 4 octets en utilisant au lieu de ﹪%.2f.
Neil
6

JavaScript (ES6), 93 octets

(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

Essayez-le en ligne!

Comment?

Nous calculons récursivement une somme de valeurs de billets / pièces jusqu'à ce qu'elle tombe dans la plage acceptable, en essayant toujours la valeur la plus élevée en premier.

{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
la source
4

Python 3.x: 266 185 octets

Une simple modification de mon exemple de programme dans la question. Notez que la sortie n'est plus formatée pour nécessiter 2 décimales.

Edit: Merci à Jo King de l'avoir réduit.

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
la source
1
Un peu de golf pour arriver à 185 octets
Jo King
4

Java 10, 186 185 octets

(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);}

Prend les pourcentages minimum et maximum en /100décimales (c.- 15%à - d.0.15 ).

-1 octet pour résoudre le problème avec 3.51une sortie potentielle et jouer au golf pour corriger les erreurs d'arrondi de 1 octet en même temps.

Essayez-le en ligne.

Explication:

(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
la source
Je ne pense pas que ce soit techniquement valable pour le moment car la question spécifie un programme, mais le PO n'a pas clarifié.
2018 8urous
1
OP a clarifié les fonctions sont désormais autorisées, vous n'avez donc pas à vous soucier de doubler la taille.
Julurous
3

Nettoyer , 207156 octets

Passer à une fonction a économisé 51 octets, sans surprise.

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

Essayez-le en ligne!

Οurous
la source
2
OP précise que les fonctions sont désormais autorisées.
Laikoni
@Laikoni Merci de m'avoir prévenu :) Économise beaucoup d'octets - les programmes complets coûtent cher en Clean!
Julurous
2

Python ( 264 222 octets)

Un peu plus de golf.

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]

Essayez-le en ligne!

Coton Zachary
la source
2

Perl 6 , 93 92 89 octets

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

Essayez-le en ligne!

Bloc de code anonyme qui prend trois arguments (prix, pourcentage minimum et pourcentage maximum) et renvoie l'astuce.

Jo King
la source
1

Wolfram Language (Mathematica) , 105 octets

Cela donnera toutes les solutions avec un nombre minimal de pièces.

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

Essayez-le en ligne!

Kelly Lowder
la source
0

Kotlin , 215 octets

{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}

Essayez-le en ligne!

JohnWells
la source
0

Gelée ,  33  32 octets

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

Un lien monadique acceptant une liste [cost in cents, [minimum ratio, maximum ratio]] qui donne un montant de pourboire en cents.

Essayez-le en ligne!

Comment?

La première ligne est un lien d'aide qui donne le montant donné moins la plus grande note / pièce de monnaie:

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

Le nombre d'appels requis pour atteindre zéro est utilisé pour trier la plage des montants de pourboire, puis le plus à gauche est généré:

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
la source