Python 修正硬币兑换挑战中我的算法中的记忆

Python 修正硬币兑换挑战中我的算法中的记忆,python,algorithm,recursion,dynamic-programming,Python,Algorithm,Recursion,Dynamic Programming,我知道关于这个挑战有很多线索,但是在标记为重复之前,请继续阅读 需要找到所有不同的方式进行改变,给出一个硬币清单。 我用递归编写了一个解决方案,虽然有效,但效率很低。我想添加备忘录。我知道还有其他方法(例如)可以解决这个问题,但为了让我了解事情是如何运作的,我正在寻求您的帮助,以解决我在解决方案中发现的问题 首先,代码如下: d = {} def make_change(n, coins): print n # base case if n < 0:

我知道关于这个挑战有很多线索,但是在标记为重复之前,请继续阅读

需要找到所有不同的方式进行改变,给出一个硬币清单。 我用递归编写了一个解决方案,虽然有效,但效率很低。我想添加备忘录。我知道还有其他方法(例如)可以解决这个问题,但为了让我了解事情是如何运作的,我正在寻求您的帮助,以解决我在解决方案中发现的问题

首先,代码如下:

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])
d={}
def兑换(n,硬币):
印刷品
#基本情况
如果n<0:
打印“否”
打印'.'*40
返回0
如果n==0:
打印“耶”
打印'.'*40
返回1
如果在d中为n:
返回d[n]
其他:
c=0
对于硬币中的i:
打印“i=”,i

c+=作出改变(n-i,[c代表硬币中的c如果c我认为你的逻辑不完全正确。你应该为每一个数量收集解决方案。另外,使用
n
coins
进行记忆是有意义的,同时只允许不大于当前硬币的硬币,以免产生相同变化的排列:

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])
[]
从集合导入defaultdict
d=默认dict(dict)
def兑换(n,硬币):
#基本情况
如果n<0:
返回[]#没有可能的解决方案
如果n==0:
返回[[]]#一个解决方案:空列表
#递归
sols=[]#收集解决方案
#制作可散列的备忘录键,并保证从较大的硬币开始
tpl=元组(已排序(硬币,反面=真))
如果第三方物流不在d[n]:
对于第三方物流中的c:
#仅允许硬币>兑换(20、[10,5])
[[10, 10], [10, 5, 5], [5, 5, 5, 5]]
>>>做出改变(25、[10,5])
[[10, 10, 5], [10, 5, 5, 5], [5, 5, 5, 5, 5]]
>>>做出改变(30、[10,5])
[[10, 10, 10], [10, 10, 5, 5], [10, 5, 5, 5, 5], [5, 5, 5, 5, 5, 5]]
>>>做出改变(27、[10,5])
[]

我认为你的逻辑并不完全清晰。你应该为每一笔金额收集解决方案。同时,将
n
coins
用于记忆,同时只允许不大于当前硬币的硬币,以免产生相同变化的排列:

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])
[]
从集合导入defaultdict
d=默认dict(dict)
def兑换(n,硬币):
#基本情况
如果n<0:
返回[]#没有可能的解决方案
如果n==0:
返回[[]]#一个解决方案:空列表
#递归
sols=[]#收集解决方案
#制作可散列的备忘录键,并保证从较大的硬币开始
tpl=元组(已排序(硬币,反面=真))
如果第三方物流不在d[n]:
对于第三方物流中的c:
#仅允许硬币>兑换(20、[10,5])
[[10, 10], [10, 5, 5], [5, 5, 5, 5]]
>>>做出改变(25、[10,5])
[[10, 10, 5], [10, 5, 5, 5], [5, 5, 5, 5, 5]]
>>>做出改变(30、[10,5])
[[10, 10, 10], [10, 10, 5, 5], [10, 5, 5, 5, 5], [5, 5, 5, 5, 5, 5]]
>>>做出改变(27、[10,5])
[]
为了澄清问题(无法评论-抱歉),该行

c+=make_change(n-i,[c代表硬币中的c,如果c只是为了澄清(不能评论-对不起),行


c+=make_change(n-i,[c代表硬币中的c,如果cI不确定我是否遵循。问题是,由于已经有了针对d[10]的解决方案,任何其他解决方案都被拒绝。列表的顺序IIUC仅更改(或可以更改)哪种解决方案被采用了,哪种被遗漏了。上述解决方案是基于我写的一个解决方案,没有记忆。如果cI试图解释你第一次尝试计算d时发生的情况,那么硬币中的[c代表c[10]-目前还没有解决方案-为什么只得到1个解决方案?这是因为在上一步中,您只剩下大小为5的硬币,这是因为您的硬币是从小到大顺序排列的。也许某种说明会有所帮助:首先您要计算d[20],您有硬币(5,10),现在您使用的是硬币5(因为它是您的硬币列表中的第一个),然后您调用d[15],只有类型(5)的硬币可用(
cI我不确定是否遵循)。问题是,因为已经有了d[10]的解决方案,所以任何其他解决方案都被拒绝。列表的顺序IIUC仅更改(或可以更改)哪种解决方案被采用了,哪种被遗漏了。上述解决方案是基于我写的一个解决方案,没有记忆。如果cI试图解释你第一次尝试计算d时发生的情况,那么硬币中的[c代表c[10]-目前还没有解决方案-为什么只得到1个解决方案?这是因为在上一步中,您只剩下大小为5的硬币,这是因为您的硬币是从小到大顺序排列的。也许某种说明会有所帮助:首先您要计算d[20],您有硬币(5,10),现在您使用的是硬币5(因为它是您的硬币列表中的第一个),然后您调用d[15],只有类型(5)的硬币可用(
c感谢您的回复。我试图理解这是如何工作的,从某种意义上说,是什么组件确保了可以用于达到目标的相同硬币元组的所有唯一组合。这对我来说并不简单。同时,我想说声谢谢。是的,理解这样一个递归实现可能很困难冷。您应该添加一些调试打印语句。但归根结底,它的简单性相当令人满意;)收集所有解决方案的是,它在所有硬币上循环,并将每个硬币与不包含该硬币的金额的递归解决方案相结合。它在
sols
中收集所有生成的组合。感谢您的回复。我试图理解这是如何工作的,从哪个组件确保所有唯一组合的意义上说包括了可以用来达到目标的同一组硬币。这对我来说并不简单。同时,我想要