分而治之递归解决方案

时间:2019-05-09 16:14:57

标签: python divide-and-conquer

我正在尝试实施分而治之的程序,当给定硬币集c = {c0,c1,...,cn}和金额A时,它发现可以支付多少种不同的方式A,如以及重复执行该功能的次数。

我的想法是要做这样的事情:

callsMade = 0
coins = [1,5,10,25]

def makeChange(A, c):
    global callsMade
    callsMade += 1
    if(A == 0):
        return 1
    if(A < 0):
        return 0
    combos = 0
    for i in range(len(coins)):
        combos += makeChange(A - coins[i], i)
    return combos

其中A是要传入的金额,c = len(coins)-1。 但是,此代码段的行为不符合我的预期。我的想法是遍历硬币阵列,用该阵列位置的硬币减去当前数量,然后递归调用makeChange函数,该函数具有较少的数量和数组中的下一个硬币,然后使全局调用分别增加1时间。

使用硬币集= [1,5,10,25]且金额A = 200,组合的数量应为1463,并发出约1500次呼叫。

2 个答案:

答案 0 :(得分:1)

重复关系看起来像这样(为简洁起见,我删除了呼叫计数器):

def makeChange(A, coins, k=0):
    if A == 0: return 1
    if A <  0: return 0
    return sum(makeChange(A - coins[i], coins, i) for i in range(k, len(coins)))

也就是说,您不会考虑比已经使用的硬币小的硬币,否则您将获得[1, 1, 5][1, 5, 1]之类的组合。有了这个,我得到makeChange(200, (1,5,10,25))的1463个组合,总共有111491个呼叫,比您的预期要多一些。

请注意,此函数将多次计算许多组合。例如,您可以通过A=194[1,5]到达[1,1,1,1,1,1],依此类推,但是makeChange(194, coins, k=1)的结果在两种方式上都是相同的。您可以使用functools.lru_cache自动记住这些值。这样,您只需拨打801次电话,即可获得相同的结果。

@functools.lru_cache(None)
def makeChange(A, coins, k=0):
    # same as above

(为便于记忆,您必须将coins作为参数包含(作为tuple,而不是list,以便可以进行哈希处理),否则它将重用结果换一组不同的硬币。)

答案 1 :(得分:0)

基本思想是正确的,但是您需要考虑递归的一些问题以及要算作正确答案的内容。

从简单开始,您可以询问[1,5,10,25]的多少组合应等于6

应该是3:
[1, 1, 1, 1, 1, 1], [5, 1], [1, 5]
或2:
[1, 1, 1, 1, 1, 1], [1, 5]

两个对我来说最有意义。为此,您需要将硬币数组的子集传递回递归,因此,当您在for循环中查看5时,您无需再考虑[5, 1] -假设您已经算过{{1} } 在此刻。而不是传递未使用的[1, 5]参数,而是传递c列表。然后在循环中管理该列表。在这里,我添加了一个额外的参数coins来收集组合,以帮助检查工作。如果您只想要计数,则不需要。

cur

结果:

def makeChange(A, coins, cur = None):
    ''' This will also collect the combinations to check the work'''
    if cur is None:
        cur = []
    global callsMade
    callsMade += 1
    if(A == 0):
        print(cur)
        return 1
    if(A < 0):
        return 0
    combos = 0
    for i, coin in enumerate(coins):
        if coin > A: # don't bother if coin is too big
            continue
        # pass a subset of the list into recursion
        combos += makeChange(A - coin, coins[i:], cur + [coin])
    return combos

coinset = [1,5,10,25]
A = 10
makeChange(A, coinset)

[1, 1, 1, 1, 1, 1, 1, 1, 1, 1] [1, 1, 1, 1, 1, 5] [5, 5] [10] Out[456]: 4 设置为A会显示200组合。