Oblicz napiwek, używając najmniejszej liczby monet

23

Większość aplikacji kalkulacyjnych ze wskazówkami po prostu bierze stały procent ceny posiłku. Na przykład, jeśli Twój posiłek kosztuje 23,45 USD, możesz zostawić napiwek 15% = 3,52 USD lub bardziej hojny napiwek 20% = 4,69 USD.

Wystarczająco wygodne dla użytkowników kart kredytowych. Ale nie tak, jeśli wolisz zostawić napiwki, w takim przypadku te nieparzyste centy przeszkadzają. Zmodyfikujmy więc ten pomysł, aby był wygodniejszy dla użytkowników gotówki.

Twoj przydział

Napisz, możliwie jak najmniej bajtów, program lub funkcję, która przyjmuje dane wejściowe:

  • Cena posiłku
  • Minimalny procent napiwków
  • Maksymalny procent napiwków

I wypisz dowolną ilość napiwków w zakresie [cena * min_percentage / 100, cena * max_percentage / 100], która minimalizuje liczbę wymaganych rachunków / banknotów i monet.

Załóżmy, że nominały pieniężne w USA to 1 ¢, 5 ¢, 10 ¢, 25 ¢, 1 USD, 5 USD, 10 USD, 20 USD, 50 USD i 100 USD.

Przykład

Oto przykładowy program do gry w golfa w Pythonie:

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)))

Niektóre przykładowe dane wejściowe i wyjściowe:

~$ 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
źródło
8
Jeśli nie używasz karty kredytowej, to płacisz gotówką, prawda? Czy zatem suma czek + napiwek nie byłaby odpowiednią kwotą, a nie tylko napiwek?
Sparr
4
a program that takes as input (stdin, command-line arguments, or GUI input box, whichever is most convenient in your language)Czy ma to na celu zastąpienie naszych wartości domyślnych dla wejść i wyjść? To znaczy, czy np. Funkcja, która przyjmuje trzy liczby i zwraca wynik, jest dozwolona?
Laikoni
3
Czy mam rację, że to mówię 3.51i czy 3.75są również prawidłowe dane wyjściowe dla przypadku testowego 23.45 15 17? Używają tej samej ilości monet i są również w zasięgu.
Kevin Cruijssen
3
@Sparr Nawet ludzie, którzy płacą rachunek kartą, lubią zostawić napiwek; podane są różne powody, więc nie będę ich tutaj powtarzał.
Neil
3
@Laikoni: Zredagowałem wymagania, aby używać domyślnego „programu lub funkcji” witryny, dlatego też z mocą wsteczną akceptuję istniejące odpowiedzi tylko dla funkcji.
dan04,

Odpowiedzi:

3

Węgiel , 60 bajtów

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

Wypróbuj online! Pobiera dane wejściowe jako miejsca dziesiętne. Link jest do pełnej wersji kodu. Wyjaśnienie:

Nθ

Wprowadź rachunek.

≔×θNη≔×θNζ

Wprowadź ułamki dziesiętne końcówki i oblicz minimalną i maksymalną końcówkę.

≔⁰θ

Zacznij od zera.

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

Ciąg SEXy rozwija się, do 10050.20.10.5.01.0.250.1.05.01którego jest podzielony na trzyosobowe grupy i rzutowany na pływający.

W‹θη≧⁺ιθ

Dodaj tyle aktualnego nominału, ile potrzeba, aby osiągnąć minimalną wskazówkę.

¿›θζ≧⁻ιθ»

Usuń jeden nominał, jeśli maksymalna końcówka została przekroczona.

﹪%.2fθ

Sformatuj wskazówkę do wyświetlenia.

Neil
źródło
1
Nie sądzę, aby formatowanie było wymogiem (a raczej czymś, co robi przykładowy kod).
Jonathan Allan
@JonathanAllan Cóż, w takim przypadku możesz zapisać 4 bajty, używając zamiast ﹪%.2f.
Neil
6

JavaScript (ES6), 93 bajty

(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

Wypróbuj online!

W jaki sposób?

Rekurencyjnie obliczamy sumę wartości banknotów / monet, aż znajdzie się ona w dopuszczalnym zakresie, zawsze najpierw próbując najwyższej wartości.

{b0,,bn}

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

don

{do0=10000don+1=don(ren+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
źródło
4

Python 3.x: 266 185 bajtów

Prosta modyfikacja mojego przykładowego programu w pytaniu. Zauważ, że wynik nie jest już sformatowany, aby wymagał 2 miejsc po przecinku.

Edycja: Podziękowania dla Jo King za zmniejszenie.

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
źródło
1
Kilka szybkich gry w golfa, aby dostać się do 185 bajtów
Jo King
4

Java 10, 186 185 bajtów

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

Przyjmuje minimalne i maksymalne wartości procentowe jako miejsca /100dziesiętne (tj. 15%Jako 0.15).

-1 bajt, aby rozwiązać problem z 3.51potencjalnymi danymi wyjściowymi i sposób na poprawienie błędów zaokrąglania o 1 bajt jednocześnie.

Wypróbuj online.

Wyjaśnienie:

(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
źródło
Nie sądzę, aby było to obecnie technicznie ważne, ponieważ pytanie określa program, ale OP nie wyjaśnił.
Οurous
1
OP ma wyjaśnione funkcje są teraz dozwolone, więc nie musisz się martwić o podwojenie rozmiaru.
Οurous
3

Czysty , 207 156 bajtów

Nic dziwnego, że zamiana na funkcję pozwoliła zaoszczędzić 51 bajtów.

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

Wypróbuj online!

Obrzydliwe
źródło
2
OP wyjaśniła, że ​​funkcje są teraz dozwolone.
Laikoni
@Laikoni Dzięki za poinformowanie mnie :) Oszczędza dużo bajtów - pełne programy są drogie w Clean!
Οurous
2

Python ( 264 222 bajtów)

Nieco bardziej golfa.

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]

Wypróbuj online!

Zachary Cotton
źródło
2

Perl 6 , 93 92 89 bajtów

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

Wypróbuj online!

Anonimowy blok kodu, który pobiera trzy argumenty (cena, minimalny procent i maksymalny procent) i zwraca wskazówkę.

Jo King
źródło
1

Wolfram Language (Mathematica) , 105 bajtów

To da wszystkie rozwiązania przy minimalnej liczbie monet.

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

Wypróbuj online!

Kelly Lowder
źródło
0

Kotlin , 215 bajtów

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

Wypróbuj online!

JohnWells
źródło
0

Galaretka ,  33  32 bajty

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

Monadyczny link akceptujący listę, [cost in cents, [minimum ratio, maximum ratio]]która daje napiwek w centach.

Wypróbuj online!

W jaki sposób?

Pierwszy wiersz to link pomocniczy, który daje podaną kwotę pomniejszoną o największą banknot / monetę o nominale:

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

Liczba połączeń wymagana do osiągnięcia zera jest używana do sortowania zakresu ilości napiwków, a następnie jest uzyskiwana skrajna lewa strona:

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
źródło