【问题标题】:Fix the memoization in my algo for the coin change challenge修复我的算法中用于硬币找零挑战的记忆
【发布时间】:2018-06-12 01:37:04
【问题描述】:

我知道有很多关于这个挑战的话题,但在标记为重复之前,请继续阅读。

在给定硬币列表的情况下,需要找到所有不同的方法来找零。 我写了一个有效的递归解决方案,但效率很低。我想添加备忘录。我知道还有其他方法(例如here)来解决它,但是为了我对事情如何运作的理解,我正在寻求您的帮助来解决我在 my 解决方案中发现的问题。

首先,代码如下:

d = {}

def make_change(n, coins):
    print n

    # base case
    if n < 0:
        print 'Nope'
        print '_'*40
        return 0

    if n == 0:
        print 'yay'
        print '_'*40
        return 1

    if n in d:
        return d[n]


    else:
        c = 0
        for i in coins:
            print 'i = ', i
            c += make_change(n-i, [c for c in coins if c<=i])  # https://stackoverflow.com/a/33425875/5056689

    d[n] = c
    return c


make_change(20, [5,10])

这会返回 2 个解,打印语句显示解是 (5,5,5,5) 和 (10,10)。第三种可能的解决方案 (10,5,5) 不包括在内,因为 10 已经在键中。

那么,我如何在不实际跟踪所有解决方案的情况下,用 unique 方式达到某个目标的数量来保存字典,这会破坏目的。

感谢您的帮助。

【问题讨论】:

    标签: python algorithm recursion dynamic-programming


    【解决方案1】:

    我认为你的逻辑并不完全正确。您应该收集每个数量的解决方案。同时使用ncoins 进行记忆也是有意义的,同时只允许不大于当前硬币的硬币,以免产生相同变化的排列:

    from collections import defaultdict
    d = defaultdict(dict)
    
    def make_change(n, coins):
        # base cases
        if n < 0:
            return []  # no possible solution
        if n == 0:
            return [[]]  # one solution: empty list
        # recursion
        sols = []  # solutions are to be collected
        # make hashable memo key, and guarantee to start with the bigger coins
        tpl = tuple(sorted(coins, reverse=True))  
        if tpl not in d[n]:
             for c in tpl:
                 # Only allow coins <= c for the recursion, not to get permutations
                 # of the same change, e.g. [10, 5] and [5, 10]
                 for sol in make_change(n-c, [x for x in tpl if x <= c]):
                     sols.append([c] + sol)
             d[n][tpl] = sols        
        return d[n][tpl]
    
    >>> make_change(20, [10, 5])
    [[10, 10], [10, 5, 5], [5, 5, 5, 5]]
    >>> make_change(25, [10, 5])
    [[10, 10, 5], [10, 5, 5, 5], [5, 5, 5, 5, 5]]
    >>> make_change(30, [10, 5])
    [[10, 10, 10], [10, 10, 5, 5], [10, 5, 5, 5, 5], [5, 5, 5, 5, 5, 5]]
    >>> make_change(27, [10, 5])
    []
    

    【讨论】:

    • 感谢您的回复。我试图了解这是如何工作的,从哪个组件确保可用于达到目标的相同硬币元组的所有独特组合的意义上说。这对我来说并不简单。同时,想说声谢谢。
    • 是的,理解这样的递归实现可能具有挑战性。您应该添加一些调试打印语句。但归根结底,简单性相当漂亮;)
    • 收集所有解决方案的事实是,它遍历所有硬币并将每个硬币与没有该硬币的数量的递归解决方案结合起来。它收集sols 中的所有结果组合。
    【解决方案2】:

    只是为了澄清事情(无法评论 - 抱歉),这条线

    c += make_change(n-i, [c for c in coins if c&lt;=i])

    允许递归中的下一次调用仅使用小于您在当前步骤中使用的硬币的硬币。如果您的硬币是从 最大到最小排序(如 schwobaseggl 答案中所示),但否则会修剪解决方案,这是有道理的。在你的情况下发生了什么 - 是这样的:在计算 d[10] 时,因为使用的第一个硬币是 5,你消除了使用价值 10 硬币的选项,因此只有一个解决方案可以给出 10 的变化 - a (5,5) 改变。

    为了更容易调试 - 您应该打印整个(小)表“d”,而不是在代码中间打印的所有参数。

    【讨论】:

    • 我不确定我是否遵循。问题是由于已经有 d[10] 的解决方案,任何其他解决方案都被拒绝了。列表的顺序 IIUC 只会更改(或可以更改)哪些解决方案进入以及哪些被排除在外。上面的解决方案是基于我写的没有记忆的解决方案。 [c for c in coin if c
    • 我试图解释你第一次尝试计算 d[10] 时会发生什么 - 还没有解决方案 - 为什么你只得到一个解决方案?这是因为在上一步中,您只剩下 5 号硬币,这是因为您的硬币是从小到大排列的。也许某种说明会有所帮助:起初您想计算 d[20] 并且您有硬币 (5,10),现在您使用硬币 5(因为它是硬币列表中的第一个),然后您调用d[15] 只有类型 (5) 的硬币可用 (c&lt;=i) 。然后 d[10] 和 (5) 和儿子。这就是为什么你只能得到 d[10] 的 1 个解决方案。
    猜你喜欢
    • 1970-01-01
    • 2015-07-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多