我正在尝试实施分而治之的程序,当给定硬币集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次呼叫。
答案 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
组合。