给出一个整数列表,例如
l = [3, 5, 8, 13]
和一个整数,例如
m = 13
计算sum = m的整数组合的数量,例如
combinations = len([[8, 5], [5, 8], [13], [3, 5, 5], [5, 5, 3], [3, 5, 3], ...])
对于m的小值,我可以使用这种递归(类斐波纳契线性递归关系):
def RecursiveCount(m, integers):
count = 0
if m < 0:
return 0
if m == 0:
return 1
for i in integers:
count += RecursiveCount(m-i, integers)
return count
但是对于较大的l和m,它会变慢并建议使用动态编程来记忆已经解决的组合以减少递归调用。不幸的是,我无法实现这一点。我试过读这个,但没有帮助https://bio.informatik.uni-jena.de/wp/wp-content/uploads/2014/09/book_handout_3.pdf
编辑:如果我能够使用动态编程实现它,那么学习成果将是最好的
答案 0 :(得分:3)
您可以通过将@functools.lru_cache
装饰器添加到递归函数来轻松添加记忆。
li
这会自动缓存某些参数的结果,并在再次调用该函数之前先检查该缓存,从而大大减少调用次数,从而减少运行时间。但是,这要求所有参数都是可清除的,即您必须将@functools.lru_cache()
def RecursiveCount(m, integers):
...
作为integers
传递。
tuple
的示例:结果:518,145;没有记忆的函数调用:4,672,513;记忆:29。
(如果这是用于DP的练习,这可能不是一个选项,但在实践中这很有效。)
答案 1 :(得分:2)
这是一个简单的搜索时间O(n * k)
,其中n
是总和,k
是列表中的整数数。搜索空间可以限制在O(max(list))
,但为方便起见,我们只能使用O(n)
。
Python代码:
def f(n, nums):
m = [0 for i in range(n + 1)]
for s in range(min(nums), n + 1):
for c in nums:
if c == s:
m[s] += 1
if c < s:
m[s] += m[s - c]
return m[n]
nums = (3, 5, 8, 13, 15, 20)
m = 200
t0 = time.time()
x = num_seq_sum(m, nums) # jdehesa's code
t1 = time.time()
print(x, t1-t0) # 233354368688517335733 4.085544586181641
t0 = time.time()
x = f(m, nums)
t1 = time.time()
print(x, t1-t0) # 233354368688517335733 0.0004315376281738281
t0 = time.time()
x = RecursiveCount(m, nums) # using @functools.lru_cache()
t1 = time.time()
print(x, t1-t0) # 233354368688517335733 0.0006241798400878906
答案 2 :(得分:1)
此解决方案不使用动态编程,但速度要快得多:
import math
from functools import reduce
def num_seq_sum(m, nums):
if m == 0:
return 1
# Avoid repeated numbers
nums = list(set(nums))
# Begin with no numbers picked
base = [0] * len(nums)
return sum(_seqs_sum_rec(m, nums, base, 0))
def _seqs_sum_rec(m, nums, current, i):
if i >= len(nums):
raise StopIteration
# Try without adding current number, add next numbers
yield from _seqs_sum_rec(m, nums, current, i + 1)
# Try adding current number
n = nums[i]
# While we can fit more copies of current number
while m > n:
current[i] += 1
m -= n
yield from _seqs_sum_rec(m, nums, current, i + 1)
# If we can fit exactly one more
if m == n:
current[i] += 1
# Number of permutations of the current combination
yield _num_permutations(current)
# Undo additions for parent call
current[i] = 0
def _num_permutations(comb):
return math.factorial(sum(comb)) // reduce(lambda a, b: a * b, (math.factorial(c) for c in comb), 1)
nums = [3, 5, 8, 13]
m = 13
print(RecursiveCount(m, nums) == num_seq_sum(m, nums))
# True
小型性能测试:
nums = [1, 3, 5, 8, 10, 15]
m = 30
%timeit RecursiveCount(m, nums)
# 1.48 s ± 6.86 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit num_seq_sum(m, nums)
# 4.77 ms ± 85.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)