我有两个函数fib1
和fib2
来计算Fibonacci。
def fib1(n):
if n < 2:
return 1
else:
return fib1(n-1) + fib1(n-2)
def fib2(n):
def fib2h(s, c, n):
if n < 1:
return s
else:
return fib2h(c, s + c, n-1)
return fib2h(1, 1, n)
fib2
正常工作,直到它超出递归限制。如果理解正确,Python不会针对尾递归进行优化。我很好。
即使fib1
值非常小,n
让我开始放慢速度也会停止。为什么会这样?为什么它在缓慢之前没有达到递归限制?
答案 0 :(得分:6)
基本上,你通过一遍又一遍地计算相同n值的fib1来浪费大量时间。您可以轻松地记住这个功能
def fib1(n, memo={}):
if n in memo:
return memo[n]
if n < 2:
memo[n] = 1
else:
memo[n] = fib1(n-1) + fib1(n-2)
return memo[n]
您会注意到我使用空dict作为默认参数。这通常是一个坏主意,因为相同的dict被用作每个函数调用的默认值。
在这里,我利用它来记住我计算的每个结果
您还可以使用0和1填充备忘录,以避免需要n < 2
测试
def fib1(n, memo={0: 1, 1: 1}):
if n in memo:
return memo[n]
else:
memo[n] = fib1(n-1) + fib1(n-2)
return memo[n]
哪个成为
def fib1(n, memo={0: 1, 1: 1}):
return memo.setdefault(n, memo.get(n) or fib1(n-1) + fib1(n-2))
答案 1 :(得分:5)
你的问题不是python,而是你的算法。 fib1
是tree recursion的一个很好的例子。您可以找到此特定算法的证明here on stackoverflow(〜θ(1.6
n
)
)。
n=30
(显然来自评论)需要大约三分之一秒。如果计算时间扩展为1.6^n
,我们希望n=100
可以采用2.1 million years.
答案 2 :(得分:2)
想想每个中的递归树。第二个版本是递归的单个分支,在函数调用的参数计算中添加,然后它返回值。如您所知,Python不需要尾递归优化,但如果尾调用优化是解释器的一部分,也可以触发尾递归优化。
另一方面,第一个版本在每个级别需要2个递归分支!因此,该功能的潜在执行次数大幅增加。不仅如此,大部分工作都重复了两次!考虑:fib1(n-1)
最终再次调用fib1(n-1)
,这与从第一个调用帧的引用点调用fib1(n-2)
相同。但是在计算出该值之后,必须再次将其添加到fib1(n-2)
的值中!所以这项工作不必要地重复多次。