【问题标题】:How does this naive recursive fibonacci implementation not stackoverflow?这种天真的递归斐波那契实现如何不是stackoverflow?
【发布时间】:2013-03-07 20:03:34
【问题描述】:

几个月前开个玩笑,我的一位同事试图通过使用这种指数算法计算斐波那契数来“加速宇宙的热寂”:

int Fib(int n)
{
    if (n <= 1)
        return 1;
    else
        return Fib(n - 1) + Fib(n - 2);
}

这如何不会导致 C# 中的堆栈溢出?我们在放弃之前设法到达了 Fib(52)(而 Fib(51) 花了很多时间)。我认为这会严重影响堆栈​​以导致堆栈溢出,因为默认情况下 CLR 仅分配 1M 给堆栈。另外,我很确定这也不符合尾递归的条件。

【问题讨论】:

  • 那么,你认为每次递归有多少字节被压入堆栈?
  • 您使用的是什么版本的 C# 编译器和框架?您是否查看过生成的 MSIL 或它得到 JIT 的 [de] 编译的汇编程序? C# 编译器完全有可能将解析树识别为斐波那契序列并进行特殊情况优化。
  • 2^52 是我的超级天真计算,呵呵,因为复杂度是 O(2^n)
  • 时间复杂度和空间复杂度不是一回事。该程序在堆栈空间中是 O(n),在时间上是 O(Fib(n))。 (关于 naive fib 的有趣之处在于,运行时间与输出的大小成正比。)
  • 另外,您没有计算第 52 个 fib 数。第 47 位以上的 Fib 数大于 int.MaxValue。你无声无息地溢出。

标签: c# .net algorithm stack-overflow fibonacci


【解决方案1】:

递归调用不是同时计算的,而是按顺序计算的,这意味着Fib(n - 2) 只会在Fib(n - 1) 之后计算(或相反)。这意味着即使您创建了2^n 递归调用,也只有n 将同时处于活动状态。因此Fib(52) 将只需要52 堆栈帧Fib 的空间,这不会占用任何明显的堆栈空间。

【讨论】:

  • +1。堆栈深度太小,无法解决任何问题。要从这样的代码中得到一些邪恶的东西,应该开始并行计算(但堆栈深度仍然不是问题)。
  • @AlexeiLevenkov 一种“加速”的建议方法是生成一个新线程来并行计算每个 fib(x)!大声笑
【解决方案2】:

天真的斐波那契实现确实会产生大量的函数调用(实际上等于结果),但它不会递归得很深。递归的最大深度为n

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-03
    • 2014-04-02
    • 2017-11-18
    • 2014-01-08
    • 2011-12-14
    • 2012-11-29
    • 2018-04-10
    相关资源
    最近更新 更多