这是尾递归吗?

4

我试图寻找关于尾递归的例子,但实在看不出常规递归与尾递归之间的区别。如果这不是尾递归,请问有人能告诉我为什么不是吗?

public static long fib(long index) {

// assume index >= 0

if (index == 0) // Base case

  return 0;

else

  if (index == 1) // Base case

    return 1;

  else

    // Reduction and recursive calls

    return fib(index - 1) + fib(index - 2);

}  // end of method fib(long index)
2个回答

16

不,问题中的方法并不使用尾递归。尾递归很容易识别:递归步骤是该方法中发生的最后一件事情

在您的代码中,在两个递归调用结束之后,还有一个加法操作需要执行。因此,该方法是递归的,但不是尾递归的

为了比较,这里是一个fib()方法的尾递归实现-请注意,我们需要传递额外的参数来保存递归状态,更重要的是,请注意,在递归调用返回后没有剩余操作需要执行。

public static long fib(int n, long a, long b) {
    return n == 0 ? b : fib(n-1, a+b, a);
}

就像这样使用:

fib(10, 1, 0) // calculates the fibonacci of n=10
=> 55

在n=92之前,以前的实现方法可以正常工作,但对于更大的数字,您需要使用 BigInteger 而不是 long ,最好切换到迭代算法。


你的回答很好,除了这一行:“所以这个方法是递归的,但不是尾递归的。” 正如你在回答开头暗示的那样,尾递归性是递归调用的属性,而不是方法的属性。 你可以选择说一个方法是TR,如果所有的调用点都是TR,但这是一个可能会让读者包括OP感到困惑的简化。 - Gene
@Óscar López - 我不熟悉那个部分"?b:"它是用来做什么的? - BluceRee
@Gene 我不明白你的意思 :( 不清楚哪个部分不清楚。如果你认为我的回答可以改进,随时编辑它 - 我会很感激的! - Óscar López
@ÓscarLópez - 再问一个问题,抱歉我很难理解这个概念。如果要获取不同于10的斐波那契数列,应该怎么做呢?我像你一样将10插入程序中,结果是55。我尝试了其他数字,但似乎不正确。您是否还需要更改1和0?如果是这样,这些数字到底是做什么的?递归对我来说非常难理解。 :( - BluceRee
@BluceRee 只需要更改第一个数字。例如,要计算15的斐波那契数列,可以这样做:fib(15, 1, 0)。希望您不要尝试为大数字计算该函数,因为斐波那契数列增长非常快,long类型将无法容纳该值。 - Óscar López
显示剩余2条评论

4

@Óscar López已经回答了这个问题。

人们之所以关心一个调用是否是尾调用,是因为有一种优化方法可以将尾调用替换为分支语句,从而避免需要创建新的堆栈帧。这可以在有限的堆栈上进行无限的尾递归。

然而,由于你使用了java标签,你应该知道当前的Java实现并没有实现尾调用优化。在Java中深度递归的方法调用可能会导致StackOverflowError


我认为IBM JDK/JRE(不记得名字了)曾经优化过尾调用。 - Javier
这很有趣。我想知道他们是如何绕过安全相关问题的,以便在Sun / Oracle / OpenJDK代码库中实现它... - Stephen C

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