【问题标题】:Coins all combinations - strange difference between 2 algorithms硬币所有组合 - 2算法之间的奇怪差异
【发布时间】:2016-07-04 23:28:12
【问题描述】:

我试图解决硬币兑换问题。因为我住在欧洲,所以让我们把它变成一个欧元问题。 我们需要 5 欧元。 我们可以使用 10、20、50 美分、1 欧元、2 欧元和 5 欧元。 5欧有多少种可能?

这是leif 发布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)

效果很好,但是因为这段代码对我来说非常困难(这个递归在这里只能在魔术的帮助下工作)我使用了另一个代码,这对我来说很容易理解。

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)

他们都给了我相同长度的答案 - 有 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)

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

所以我检查了列表结果和第三种(for 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]]

(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) 美分

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

【问题讨论】:

  • 您是否尝试打印出is_5euro(L) 针对这些情况返回的内容? This question 和其他链接的最有可能相关。
  • @Lafexlos 不是真的,只有答案列表。这就是我创建缺失元素列表的方式。如果算法适用于上述任何一项,则结果将为 50。例如 sum[0,0,0,0,1x2,48x1]。
  • 您可以尝试添加每个子集,如果加起来达到 5 欧元,则将可能性增加 1?

标签: python algorithm debugging math permutation


【解决方案1】:

问题是Floating Point Arithmetic: Issues and Limitations In Python。浮点值并不总是看起来如此。你认为

(48 x 10) + (1 x 20) cents = 5.0

但最初python认为它是5.000000000000001。不等于5.0,因此,is_5euro 返回False

你可以测试一下

sum([48*0.1, 1*0.2, 0*0.5, 0*1, 0*2, 0*5])

正如@Tom 所建议的,您应该使用整数。您可以将denom 更改为

denom = [c10 * 10, c20 * 20, c50 * 50, e1 * 100, e2 * 200, e5 * 500]

和你的is_5euro

def is_5euro(cents_list):
    return sum(cents_list) == 500

它应该可以工作。

【讨论】:

  • 观察力不错。很明显,浮点数应该用于此。这是一个谨慎的问题,所有的数学运算都应该用整数来完成。解决方案是将所有东西都以美分而不是欧元为单位,目标是 500 美分。然后你有 10、20、50、100、200 和 500 作为你的面额,都是整数。或者您可以将单位设为 10 美分,并使用 1、2、5、10、20 和 50,就像第一个解决方案一样。
  • @TomKarzes 你是对的,我也会使用整数。
  • 哇!确实如此。谢谢!我永远不会自己弄清楚。
  • 对,不用解释。我只是错过了 5.0 != (48*0.1) + (1*0.2) 聪明的错误,愚蠢的我。
【解决方案2】:

我知道你不是在寻找新的解决方案,但只是为了笑,我想我会发布我能想出的最简单的解决方案:

def coin_combos(goal, unit_list):
    if goal == 0:
        return [[0] * len(unit_list)]

    if len(unit_list) == 0:
        return []

    unit = unit_list[0]
    unit_list2 = unit_list[1:]
    max_cnt = goal // unit

    return [[i] + s for i in range(max_cnt + 1) \
                    for s in coin_combos(goal - i * unit, unit_list2)]

这是一个示例调用,使用您提供的参数。似乎找到了正确数量的解决方案:

>>> r = coin_combos(500, [500, 200, 100, 50, 20, 10])
>>> len(r)
451
>>>

【讨论】:

    猜你喜欢
    • 2016-01-25
    • 1970-01-01
    • 2011-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多