【问题标题】:How to apply Tail Call optimization如何应用尾调用优化
【发布时间】:2017-07-27 03:09:22
【问题描述】:

我写了一个递归函数,但是当我输入一个很大的数字,比如100时,递归需要很多时间。

我想在代码中应用尾调用优化,以最小化计算时间,但我做不到。

递归算法是f(n) = f(n-1) + f(n-2)

int fib(int n)
{
    if (n == 1) return n;
    if (n == 0) return n;
    return fib(n - 1) + fib(n - 2);
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::cout << "---->" << fib(3);
    std::system("PAUSE");
    return 0;
}

【问题讨论】:

  • 这是一个 XY 问题。您实际上是在计算斐波那契数,但您使用的是指数算法(约 O(2^n)
  • fac 对你的函数来说是一个糟糕的名字。你不是在计算阶乘。您正在评估斐波那契数。
  • 为了好玩,我写了一个尾递归斐波那契函数:godbolt.org/g/N8TrPc
  • 这是一个高效的尾递归斐波那契函数:godbolt.org/g/U9HfY8
  • @venaizu 第 8181 个斐波那契数有 1709 个十进制数字(5679 个二进制数字),所以你的朋友一定使用了一些任意精度整数的实现。写一个;这比计算斐波那契数要有趣得多。

标签: c++ recursion


【解决方案1】:

尾调用优化 (TCO) 仅在函数的最后一条指令立即返回另一个函数调用(可以是递归调用)的输出时起作用。但在这种情况下并非如此。

TCO 的全部目的是允许编译器通过多个函数调用恒定使用堆栈空间。 IOW,编译器可以分配单个堆栈帧(如果有)并一遍又一遍地重用它而不会弄乱任何东西。

您的fac() 函数调用自身两次,将返回值相加,然后将相加结果返回给调用者。因此,会创建局部临时变量来接收这些返回值。每个函数调用都必须创建一个新的堆栈帧来保存自己的局部变量,这不是常量 堆栈使用。而且,您的添加意味着函数的 last 指令不是函数调用。因此,您的代码否定了使用 TCO 的任何可能性。

【讨论】:

    【解决方案2】:

    您的代码基本上是这样做的:

    int a = fac(n - 1);
    int b = fac(n - 2); 
    return a + b;
    

    因此,函数返回前的最后一个操作不是函数调用,因此这里不可能使用TCO。

    【讨论】:

      猜你喜欢
      • 2011-03-31
      • 2014-04-06
      • 1970-01-01
      • 1970-01-01
      • 2011-07-11
      • 2015-04-23
      • 2013-06-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多