Python fibonacci函数中的递归堆栈溢出

Python fibonacci函数中的递归堆栈溢出,python,recursion,stack,stack-overflow,fibonacci,Python,Recursion,Stack,Stack Overflow,Fibonacci,我想了解递归函数的堆栈溢出机制。 所以我用了这个斐波那契函数: def fib(n): if n==1 or n==2: return 1 return fib(n-1)+fib(n-2) print (fib(555)) 当我尝试使用这个函数来计算fibo999时,我遇到了一个运行时错误:相比之下,超过了最大递归深度 但当我尝试计算fibo555时,python不会打印任何运行时错误,但仍然可以工作 我知道python中默认的递归深度是1000,但我不明白为什么在我试图查找FIB

我想了解递归函数的堆栈溢出机制。 所以我用了这个斐波那契函数:

def fib(n):
 if n==1 or n==2:
    return 1
 return fib(n-1)+fib(n-2)
print (fib(555))
当我尝试使用这个函数来计算fibo999时,我遇到了一个运行时错误:相比之下,超过了最大递归深度 但当我尝试计算fibo555时,python不会打印任何运行时错误,但仍然可以工作

我知道python中默认的递归深度是1000,但我不明白为什么在我试图查找FIBO555时python不打印运行时错误

THK'X对于所有的

可能是限制在堆栈深度上,而不是递归调用的数量上,并且在第一次调用fib之前,您可能已经用完了一些堆栈深度

使用fib555,您将只添加555个堆栈帧,而不是555+554。这是因为计算的两项是按顺序进行的。换句话说,fib555被调用,它使用555堆栈帧来完成它的工作,然后,这一点很重要,展开这些帧,使您回到调用fib555之前的堆栈级别。然后调用fib554,它使用大约相同的帧数

在任何阶段,您使用的堆栈帧都不会超过额外的555堆栈帧。因此,从图形上看,它没有:

它正在做的是:

_       _     _
 \     / \   /  (555 levels down, then
  \   /   \_/    back up, then down
   \_/           554, then back up again)
在任何情况下,使用递归计算斐波那契数都是非常低效的,因为对于999,计算斐波那契数需要两次,一次作为第一项斐波那契数的一部分,一次作为第一项斐波那契数的一部分,一次作为第二项斐波那契数的一部分。然后,对于这两个fib998调用中的每一个,计算fib997两次。这是一种非常耗时的方法,随着运行时间以二次方的方式上升,这个术语可能是错误的,那些有更多数学知识的人可以纠正我,但我的意思是,它随着数字的平方而上升

使用迭代解决方案要好得多,比如下面的伪代码,毫无疑问,它看起来很像Python,因为Python是一种理想的伪代码语言:

# Calculate Fibonacci, first term fib(0).

def fib(n):
    if n <= 1: return 1
    grandparent = 1
    parent = 1
    for i in range(n - 1):
        me = parent + grandparent
        grandparent = parent
        parent = me
    return me

for i in range(10):
    print i, fib(i)

与我等待递归解决方案整整十分钟的时间相反,我在感到无聊和迷茫之前等待了十分钟。

请检查递归。fib999递归调用次数不超过1000次,因为fib999=fib998+fib997…所以它会创建一个超过1000次的大而长的层次结构。@simonzack,你认为这算是一个用例吗?我甚至不认为这可以优化TCO,至少不能完全使用传统方法。@Abhi,这是错误的。通话深度约为998。重要的是隐含的堆栈帧和调用方的堆栈帧,它们降低了您实际可以使用的数量。嗨,freinds,我尝试用这个例子来更好地取消重启和递归堆栈溢出机制,我取消重启和999的情况,但不是555的情况,我认为python必须显示运行时错误,但他没有t@simonzack . 在使用递归作为主要或唯一迭代手段的语言中,尾部调用优化是必要的;Python还有其他用于迭代的习惯用法。你的尾部递归函数效率很低?不要使用尾部递归。正如Veedrac指出的,这不是一个尾部递归函数,它需要立即返回递归调用的返回值;但是,需要保存fibn-1的值,直到fibn-2返回,并且可以将两者相加。通过此函数,我试图更好地了解递归堆栈的用法。我不想重复为什么调用是555而不是555+554,我认为这两个调用共享同一个堆栈,不是吗?@user3896694,我将在回答中详细说明。但我仍然有一个问题,在python中的递归深度是1000,为什么我在尝试计算fibo995时出现运行时错误,递归函数调用的数量比limit@user3896694:对不起,我不知道,除了已经假定的,您已经用完了一些堆栈帧。或者,Python源代码中的检查可能不太准确。底线是,Python有一个阻止失控递归的限制,不管它在990或1000时发生。Guido不太喜欢递归,这已经不是什么秘密了,这可能就是为什么限制首先存在的原因,它足以允许有限的递归,当然也足以超越正常的非递归调用,但不会让它失控。thx,只需更正一下,fibonacci555函数有554和553两个以上,因为fibo554的调用将包含两个调用,它们上的每个调用也将包含另外两个调用。最后,我们将有大量的递归调用,但由于调用是按顺序进行的,因此不会超过堆栈限制
# Calculate Fibonacci, first term fib(0).

def fib(n):
    if n <= 1: return 1
    grandparent = 1
    parent = 1
    for i in range(n - 1):
        me = parent + grandparent
        grandparent = parent
        parent = me
    return me

for i in range(10):
    print i, fib(i)
70330367711422815821835254877
18354977018126983635873274260
49050871545371181969335797422
49494562611733487750449241765
99108818636326545022364710601
20533741212738673391111981393
73125598767690091902245245323
403501