我有一个递归算法,可以计算一些概率值。输入是一个整数列表和一个表示常数值的单个整数值。
例如,p([12,19,13], 2)
进行三个递归调用,分别是
p([12,19],0)
和p([13], 2)
p([12,19],1)
和p([13], 1)
p([12,19],2)
和p([13], 0)
因为2可以分解为0 + 2、1 + 1或2 + 0。然后,每个调用都采用类似的方法,并进行其他几个递归调用。
我拥有的递归算法
limit = 20
def p(listvals, cval):
# base case
if len(listvals) == 0:
return 0
if len(listvals) == 1:
if cval == 0:
return listvals[0]/limit
elif listvals[0] + cval > limit:
return 0
else:
return 1/limit
result = 0
for c in range(0,cval+1):
c1 = c
c2 = cval-c
listvals1 = listvals[:-1]
listvals2 = [listvals[-1]]
if listvals[-1] + c2 <= limit:
r = p(listvals1, c1) * p(listvals2, c2)
result = result+r
return result
我一直试图将其转换为自下而上的DP代码,但无法弄清楚进行迭代的方式。
我写下了最终结果所需计算的所有中间步骤,很明显在递归调用的底部有很多重复。
我尝试创建如下所示的预先计算值的字典
m[single_value]=[list of calculated values]
并使用这些值代替进行第二次递归调用p(listvals2, c2)
,但是就运行时间而言,它并没有太大帮助。
如何通过使用适当的自下而上的方法来缩短运行时间?
答案 0 :(得分:1)
不确定我是否了解您的程序要计算的内容,所以对此无能为力,也许还可以解释一下?
关于提高性能,您仅缓存在递归调用中重复的计算的叶节点。更好的方法是将函数p
的第一个参数作为元组而不是列表,然后将p
的两个参数的元组用作字典中的缓存键。 / p>
Python的标准库functools
提供了一种简单的方法来完成这一相当普通的工作。
from functools import wraps
def cached(func):
cache = {}
@wraps(func)
def wrapped(listvals, cval):
key = (listvals, cval)
if key not in cache:
cache[key] = func(key)
return cache[key]
return wrapped
使用此装饰器缓存所有调用功能:
@cached
def p(listvals, cval):
现在让您的p
接受元组而不是列表:
p((12,19,13), 2)