硬币所有组合 - 2种算法之间的奇怪差异

Tom*_*cik 4 python algorithm math debugging permutation

我试图解决硬币改变问题.因为我住在欧洲让我们成为欧元问题.我们需要5欧元.我们可以使用10,20,50美分,1欧元,2欧元和5欧元.有多少种可能获得5欧元?

这是leif这里发布的代码.

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)
Run Code Online (Sandbox Code Playgroud)

它工作得很好,但是因为这段代码对我来说非常困难(这个递归只在魔法的帮助下才起作用)我使用了另一个代码,这对我来说很容易理解.

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)
Run Code Online (Sandbox Code Playgroud)

他们都给了我相同的答案 - 有451种可能性.

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)
Run Code Online (Sandbox Code Playgroud)

但这个解决方案只给我们446种可能性.

所以我检查了列表结果和第三个(money for for for)算法之间的差异似乎没有考虑作为一种情况,当我们有:

>>>
[[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]]
Run Code Online (Sandbox Code Playgroud)

(48 x 10)+(1 x 20)美分
(46 x 10)+(2 x 20)美分
(4 x 10)+(23 x 20)美分
(2 x 10)+(24 x 20)美分
(41 x 10)+(2 x 20)+(1 x 50)美分

这似乎很荒谬.你知道为什么它不像第一和第二算法那样工作吗?

Muh*_*hir 5

问题是浮点运算:Python中的问题和限制.它们似乎并不总是浮动值.您认为

(48 x 10) + (1 x 20) cents = 5.0
Run Code Online (Sandbox Code Playgroud)

但最初python认为它5.000000000000001.5.0因此,is_5euro返回不等于False.

你可以测试一下

sum([48*0.1, 1*0.2, 0*0.5, 0*1, 0*2, 0*5])
Run Code Online (Sandbox Code Playgroud)

正如@Tom所建议你应该使用整数.你可以改变你denom

denom = [c10 * 10, c20 * 20, c50 * 50, e1 * 100, e2 * 200, e5 * 500]
Run Code Online (Sandbox Code Playgroud)

和你is_5euro

def is_5euro(cents_list):
    return sum(cents_list) == 500
Run Code Online (Sandbox Code Playgroud)

它应该工作.

  • 很好的观察.很明显浮点数应该*不用于此.这是一个谨慎的问题,所有数学都应该用整数来完成.解决方案是将所有东西保留在美分而不是欧元,目标是500美分.然后你有10,20,50,100,200和500作为你的面额,所有整数.或者你可以使单位10美分,并使用1,2,5,10,20和50,作为第一个解决方案. (2认同)