我经历了这个问题
Number of ways to add up to a sum S with N numbers Find all ways to sum given number (with repetitions allowed) from given set
那里的答案不太明白,
我写了两个方法来解决一个问题:
使用数字N(允许重复)找出可以达到和S的方法数
例如。 sum = 4和number = 1,2,3 answer是1111,22,1122,31,13,1212,2112,2212
在一种方法中我使用了memoization而在另一种方法中我没有使用memoization。在我的机器中,memoize版本的运行速度比非memoized版本慢
这两种解决方案都有效。
记事本版:
def find_denomination_combinations(amount, denominations):
memo = {}
def calculate_combinations(max_amt):
return_list = list()
for denomination in denominations:
new_sum = max_amt - denomination
if new_sum == 0:
return_list.append([max_amt])
return return_list
elif new_sum < 0:
return [[]]
else:
if new_sum in memo:
combi_list = memo[new_sum]
else:
combi_list = calculate_combinations(new_sum)
for combination in combi_list:
if new_sum in memo and combination[:] not in memo[new_sum]:
memo[new_sum].append(combination[:])
else:
memo[new_sum] = []
memo[new_sum].append(combination[:])
combination.append(denomination)
return_list.append(combination)
return return_list
result = calculate_combinations(amount)
return result
非备忘版本
def find_denomination_combinations_nmemo(amount, denominations):
def calculate_combinations(max_amt):
return_list = list()
for denomination in denominations:
new_sum = max_amt - denomination
if new_sum == 0:
return_list.append([max_amt])
return return_list
elif new_sum < 0:
return [[]]
else:
combi_list = calculate_combinations(new_sum)
for combination in combi_list:
combination.append(denomination)
return_list.append(combination)
return return_list
result = calculate_combinations(amount)
return result
我的算法是:
每个D的[T(sum-D)],其中D属于给定的整数集
如果输入和= 16并且整数设置= [1,2,3]
非记忆版本在0.3秒内运行,记忆版本需要5秒
答案 0 :(得分:1)
我认为memoized版本较慢,因为它用于更新最外层else
块中的备忘录dict的复杂代码。它可以更简单:
if new_sum in memo:
combi_list = memo[new_sum]
else:
combi_list = memo[new_sum] = calculate_combinations(new_sum)
for combination in combi_list:
return_list.append(combination + [denomination])
这要快得多。使用此修复程序,在大多数情况下,memoized版本应该比非memoized代码更快。
但是还有其他问题。如果您的denominations
列表未按递增顺序排序或者面额值之间存在间隙,则会得到错误的结果。基本上,任何可能导致elif
案件被击中的情况都会给出错误的结果。
这是for
循环体的一个版本,用于纠正这些问题:
new_sum = max_amt - denomination
if new_sum == 0:
return_list.append([max_amt]) # don't return here, to allow unsorted denominations!
elif new_sum > 0:
if new_sum in memo:
combi_list = memo[new_sum]
else:
combi_list = memo[new_sum] = calculate_combinations(new_sum)
for combination in combi_list:
return_list.append(combination + [denomination])
# do nothing for new_amt < 0
通过使每个调用在备忘录中保存自己的结果,而不是依赖于调用者来执行此操作,并将基本情况逻辑(对于new_sum == 0
)与记忆化。我还重命名或删除了几个变量:
def find_denomination_combinations_blckknght(amount, denominations):
memo = {0: [[]]} # prefill value for base case of calculate_combinations where amt==0
def calculate_combinations(amt):
if amt not in memo:
memo[amt] = []
for denomination in denominations:
new_amt = amt - denomination
if new_amt >= 0:
for combination in calculate_combinations(new_amt):
memo[amt].append(combination + [denomination])
# do nothing for new_amt < 0
return memo[amt]
return calculate_combinations(amount)
这稍慢,可能是因为额外的函数调用,但代码更简单,在任何地方都没有elif
或else
个案例!