递归:幕后花絮

时间:2013-08-05 21:06:29

标签: java recursion

虽然众所周知递归是“一种自称的方法”,但我倾向于想知道实际发生了什么。以经典的因子为例:

public static int fact(int n) {
    if(n == 0)
        return 1;
    else
        return n * fact(n - 1);
}

事实(5);

我知道它有点像这样:(等号表示在为该值调用函数时发生了什么)

http://postimg.org/image/4yjalakcb/

为什么递归函数会这样?计算机的哪个方面使其自身向后工作?幕后发生了什么?

作为一名学生,我觉得我们所教授的递归是浅薄而一般的。我希望这里的优秀社区帮助我在机器本身的层面上理解它。谢谢!

4 个答案:

答案 0 :(得分:7)

以下是每当您调用方法时会发生什么的简要概述:

  • 该方法的框架从堆栈中分配。
  • 框架包含所有局部变量,参数,方法的返回值。
  • 该框架位于当前方法框架的顶部,调用此方法。
  • 当方法返回时,与该方法相关的框架将从堆栈中弹出,并且调用方法将恢复运行,并返回前一个方法中的返回值。

您可以在此处了解有关框架的更多信息 - JVM Spec - Frames

在递归的情况下,同样的事情发生。暂时不要忘记你正在处理递归,并将每个递归调用作为对不同方法的调用。所以,在factorial的情况下,堆栈会像这样增长:

fact(5)
  5 * fact(4)
    4 * fact(3)
      3 * fact(2)
        2 * fact(1) 
          1 * fact(0)  // Base case reached. Stack starts unwinding.
        2 * 1 * 1
      3 * 2 * 1 * 1
    4 * 3 * 2 * 1 * 1
  5 * 4 * 3 * 2 * 1 * 1  == Final result

答案 1 :(得分:2)

如果跟踪函数调用,您将看到它是如何工作的。

E.g。

fact(3)将返回3 * fact(2)。所以java会调用fact(2)

fact(2)将返回2 * fact(1)。所以java会调用fact(1)

fact(1)将返回1 * fact(0)。所以java会调用fact(0)

fact(0)将返回1

然后fact(1)将返回1 * 1 = 1

然后fact(2)将返回2 * 1 = 2

然后fact(3)将返回3 * 2 = 6


Java像任何其他方法一样调用递归方法。

答案 2 :(得分:0)

您可能听说过一些名为“The Stack”的内容这是用于存储方法状态的内容。

我相信它也存储了调用行,因此该函数可以返回给它的调用者

假设您调用了递归函数

 - int $input = 5
 - stack.Push L
 - GOTO FOO
 - Label L

您的递归函数(没有基本情况)可能类似于以下

 - Label FOO
 - int in = $input 
 - input = in - 1
 - stack.Push in
 - stack.Push L2
 - goto FOO
 - Label L2
 - in = stack.Pop in
 - output *= in
 - goto stack.POP

答案 3 :(得分:0)

以下可能会帮助您理解。计算机不关心他是否只调用它只是计算相同的功能。一旦你理解它是什么以及为什么它适用于很多东西,比如列表,自然数等等,它们本身就是由结构递归的,所以没有任何关于递归的神奇之处。

  1. 定义:阶乘0为1
  2. 定义:数字n大于0的阶乘是该数字与其前身的阶乘的乘积。
  3. 因此

    5! = 5*4! = 5*4*3! = 5*4*3*2! = 5*4*3*2*1! = 5*4*3*2*1*0! = 5*4*3*2*1*1 = 120
    

    所以,如果你曾经通过归纳听过证据,那就是这样的:

    1. 我们为一个基本案例证明了一些财产。
    2. 我们证明,如果n的属性为真,则n的后继属性为真。
    3. 我们得出结论,这证明了该属性适用于基本案例和所有后续案例。
    4. 示例:通过归纳证明偶数的平方是4的倍数!

      1. 0的平方为0,是4的倍数。
      2. 设n是偶数,其平方n是4的倍数。然后(2+n)*(2+n) = 4+2n+2n+n²。这是4的多重,因为n²是我们的假设,4是1,2n+2n = 4n也是4的倍数,4的倍数之和是分布定律的4的倍数:4a + 4b = 4(a+b)
      3. Q.E.D。该属性保持为0(我们的基本情况),对于(2 + n),如果它适用于n。因此它适用于2,4,6,8 ......以及所有其他偶数。
      4. (更容易证明(2a)² = 4*a*a,这是4的倍数。)

        编写递归程序与通过归纳进行证明非常相似:

        1. 我们为基本情况编写计算。
        2. 对于非基本情况,我们知道如何计算结果(例如,我们知道n! = n * (n-1)!,所以我们将其写下来,因为我们需要的功能是我们刚刚写的那个!
        3. 我们可以得出结论,我们的程序将为基本案例和基本案例的任何后继者计算正确的值。如果678!仍未计算出正确的答案,那么它与我们使用的数据类型int这一事实有关,这种数据类型不适合大数字(或者,换句话来说不同) ,计算一切moulo 2 ^ 32)此外,还有一个软件坚持将一半的可用数字解释为负数。
        4. 其工作原理与计算机硬件或编程语言无关:正如我之前所说,它是手头项目(列表,树,集,自然数)的递归结构的结果。

          新手所犯的常见错误是忽略基本情况并迷失复杂性。我总是建议从基础案例开始,一旦你有了这个,你可以假设函数存在,并且可以在更复杂的情况下使用它。