I was trying to solve a coin changing problem. Since I live in Europe let’s make it an Euro problem. We need 5 Euro. We can use 10, 20, 50 cents, 1 euro, 2 euro and 5 Euro. How many possibilities there are to get 5 Euro?
That’s the code that leif posted here.
cents = 50
denominations = [50, 20, 10, 5, 2, 1]
names = {50: "", 20: "", 10 : "", 5 : "", 2 : "", 1: ""}
def count_combs(left, i, comb, add):
if add: comb.append(add)
if left == 0 or (i+1) == len(denominations):
if (i+1) == len(denominations) and left > 0:
comb.append( (left, denominations[i]) )
i += 1
while i < len(denominations):
comb.append( (0, denominations[i]) )
i += 1
print " ".join("%d %s" % (n,names[c]) for (n,c) in comb)
return 1
cur = denominations[i]
return sum(count_combs(left-x*cur, i+1, comb[:], (x,cur)) for x in range(0, int(left/cur)+1))
print count_combs(cents, 0, [], None)
It works great, but since this code is very difficult for me (this recursion works here only with the help of magic) I used another code, that is pretty simple for me to understand.
def money(goal, money_avail, money_used):
if sum(money_used) == goal:
yield money_used
elif sum(money_used) > goal:
pass
elif money_avail == []:
pass
else:
for change in money(goal,money_avail[:], money_used+[money_avail[0]]):
yield change
for change in money(goal,money_avail[1:], money_used):
yield change
results = []
for s in money(50, [1,2,5,10,20,50], []):
results.append(s)
cn = 0
for i in results:
cn+=1
i = str(i)
print cn, i
print len(results)
They both gave me the same length of answer - there are 451 possibilities.
def is_5euro(L):
return sum(L) == 5.0
moneybag = []
for c10 in range(51):
for c20 in range(26):
for c50 in range(11):
for e1 in range(6):
for e2 in range(3):
for e5 in range(2):
denom = [c10 * 0.1, c20 * 0.2, c50 * 0.5, e1 * 1, e2 * 2, e5 * 5]
if is_5euro(denom):
moneybag.append([c10, c20, c50, e1, e2, e5])
print len(moneybag)
But this solution gives us only 446 possibilities.
So I checked what’s the difference between list results and the third (moneybag for for for) algorithm doesn’t seem to be considering as a possibility a situation when we have:
>>>
[[0, 0, 0, 0, 1, 48], [0, 0, 0, 0, 2, 46], [0, 0, 0, 0, 23, 4], [0, 0, 0, 0, 24, 2], [0, 0, 0, 1, 2, 41]]
(48 x 10) + (1 x 20) cents
(46 x 10) + (2 x 20) cents
(4 x 10) + (23 x 20) cents
(2 x 10) + (24 x 20) cents
(41 x 10) + (2 x 20) + (1 x 50) cents
This seems to be ridiculous. Do you know why it doesn't work like the 1st and 2nd algorithms?
The problem is Floating Point Arithmetic: Issues and Limitations In Python. Float values are not always they seem to be. You think
(48 x 10) + (1 x 20) cents = 5.0
But originally python considers it 5.000000000000001
. Which is not equal to 5.0
, hence, is_5euro
returns False
.
You can test this by
sum([48*0.1, 1*0.2, 0*0.5, 0*1, 0*2, 0*5])
As suggested by @Tom you should use integers. You can change your denom
to
denom = [c10 * 10, c20 * 20, c50 * 50, e1 * 100, e2 * 200, e5 * 500]
and your is_5euro
to
def is_5euro(cents_list):
return sum(cents_list) == 500
and it should work.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With