用斐波那契数列理解递归

15

我试图更好地理解递归和return语句的工作原理。因此,我正在查看一段代码,旨在确定与给定项相关联的斐波那契数-在这种情况下是4。我难以理解else语句。

def f(n):
  if n == 0:
    return 0
  if n == 1:
    return 1
  else:
    return f(n-1) + f(n-2)

f(4)

我尝试使用Visualize Python来检查每一步发生了什么,但是碰到else语句时就迷失了。

它似乎是取n的值并减1,创建一个新的n值3,然后将其返回到函数定义中。因此,它似乎只返回else语句中第一个函数的返回值。然而,else语句是写成要返回两个函数f(n-1) + f(n-2)的总和,这种情况下我认为返回值应该是5?你能把两个函数加起来吗?

非常感谢您的帮助。

以下是Visualize Python中的代码链接:两个函数的总和


4
不是添加两个函数,而是添加调用函数返回的整数。每个调用都是完全独立的,特别是每个调用都有自己私有的 n 值。 - jasonharper
2个回答

40

如果不确定,就把它分解。

enter image description here

实际上,树形流程与实际控制流是反直觉的,但一旦您了解了调用序列,它就变得更清晰。这里需要理解的是,您将一个较大的计算分解为若干个小计算的总和,并在达到基本情况(即if语句)时停止。现在,您可以执行所有的小计算,并将这些小计算的结果组合成更大的结果,直到获得最终答案。

每次递归调用达到基本情况时,它将返回1或0,具体取决于命中的情况。这个值将返回给上一个调用者。为了理解,请考虑:

f(1)<sub>3</sub> + f(0)<sub>3</sub>

请注意,下标代表递归调用树的深度。调用是由f(2)2发起的。首先计算f(1)3,并将1返回给f(2)2。然后计算f(0)3,并将0返回给f(2)2。两个返回值相加,结果为1

f(2)2然后向调用它的人返回1,在这种情况下,是f(3)1f(3)1调用了f(2)2 + f(1)2,同时另一个f(1)2也向f(3)1返回1,现在将其与f(2)2的结果相加,得到2

f(3)1现在将2传递给它的调用者f(4)0,后者恰好调用了f(3)1 + f(2)1,如此反复。


另一种看待这个问题的方法是从实际进行的第一个函数调用开始:f(4)0

f(4)0计算f(3)1 + f(2)1。但要计算f(3)1,它需要知道f(2)2 + f(1)2,同样,要计算f(2)1,它需要知道f(1)2 + f(0)2,等等。


这太棒了。谢谢你。我仍然对else语句中的'+'符号感到困惑,它是一个运算符,但在else语句中显然不起作用。有人能为此提供解释吗?另外,我使用的可视化工具没有显示两个函数同时运行。它只显示第一个函数的结果。不知道为什么会这样,但这就是让我困惑的原因。 - efw
1
@efw 我理解你的想法。就像我提到的,递归树与实际调用堆栈有些反直觉。它更像是 f(4)0 -> f(3)1 -> f(2)2 -> f(1)3 -> 1 -> f(0)3 -> 0 -> f(1)2 -> ... 等等。在 Python 中从左到右进行评估。当检查递归树时,我们按层遍历。这不是执行过程中实际的操作方式。 - cs95
我现在认为我理解了。代码首先通过递归过程,在else语句中构建一个包含n个值的“列表”。最终,当n个值满足两个条件之一时,整数值被返回,这些值的总和在最后一步被求和。再次感谢您的帮助。 - efw

4

添加一些打印语句也可以帮助澄清顺序:

def f(n):
    print("Number received:", n)
    if n == 0:
        return 0
    if n == 1:
        return 1
    else:
        print("---- First recursion ----")
        a = f(n-1)
        print("---- Second recursion ----")
        b = f(n-2)
        print(" a=:",a,"; b=",b,"; returning:", a+b)
        return a + b

print("Final f(4)=", f(4))

输出:

Number received: 4
---- First recursion ----
Number received: 3
---- First recursion ----
Number received: 2
---- First recursion ----
Number received: 1
---- Second recursion ----
Number received: 0
 a=: 1 ; b= 0 ; returning: 1
---- Second recursion ----
Number received: 1
 a=: 1 ; b= 1 ; returning: 2
---- Second recursion ----
Number received: 2
---- First recursion ----
Number received: 1
---- Second recursion ----
Number received: 0
 a=: 1 ; b= 0 ; returning: 1
 a=: 2 ; b= 1 ; returning: 3
Final f(4)= 3

这段代码无法处理输入中的大数字。您应该考虑运行时间。 - Akshay Sapra
2
@Akshay Sapra,我认同避免记忆化会使函数缺乏灵活性,但OP询问的是“返回语句如何工作”。这里很好地解决了递归的基本原理,这正是问题所在。 - Daniel Scott

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接