这个递归斐波那契的大 O 时间复杂度?
Posted
技术标签:
【中文标题】这个递归斐波那契的大 O 时间复杂度?【英文标题】:Big-O time complexity for this recursive Fibonacci? 【发布时间】:2018-05-31 22:46:08 【问题描述】:我有一个使用递归打印斐波那契数列的程序。有更好的方法,但我被要求使用递归,所以我不得不这样做。
这是程序:
#include <stdio.h>
#define TERMS 10
long fibo(int);
int main(void)
for(int i = 1; i <= TERMS; i++)
printf("%ld", fibo(i));
return 0;
long fibo(int n)
if (n < 3)
return 1;
else
return fibo(n - 1) + fibo(n - 2);
我知道这对于斐波那契数列来说确实是一种糟糕的方法,从上面可以清楚地看出这一点,因为 TERMS 超过 35,程序需要很长时间才能完成。
我已经通过this answer 并且无法理解他们是如何解决它的,但看起来像
fibo(int n) 的时间复杂度为 O(2^n)
再一次,我可能完全错了,但我只想:
这个完整程序的时间复杂度是多少,简要说明你是如何计算的?
如果您有更好的使用递归计算斐波那契的方法,也欢迎。
【问题讨论】:
我认为递归斐波那契在 O (n^6) 附近,因为直接计算表明它是 n^6 的最高项,但我无法检查。 Why is the complexity of computing the Fibonacci series 2^n and not n^2?的可能重复 你到底是从哪里得到 n^6 的?!它是指数型的,因为它需要与计算值一样长的时间(所有递归分支都以值为 1 的叶子结尾) 我认为来自比内的公式 :) @Dragonthoughts 【参考方案1】:c(fibo(n)) = c(fibo(n - 1)) + c(fibo(n - 2)) + O(1)
请注意,复杂度遵循与序列一样的精确公式,因为所有计算分支始终以值为 1 的叶结尾,因此可以通过斐波那契数列本身的封闭公式准确计算出精确 (theta) 复杂度
但这超出了您的问题范围,我们需要注意的是,
c(fibo(n))
我们现在只需要解决由
定义的上界级数an = 2 * an-1 (a1,2 = 1)
结果
an = 2^n
所以,你得到了你想要的 2^n 的上 O 界。
如果你运行它几次你会得到
sigma(c(fib(n))) 从 1 到 TERMS = O(2^(TERMS + 1) - 1)
这是一个简单的数学事实,这意味着在你的情况下(TERMS = 10)你得到
2^11 - 1 = 2047
至于您关于递归执行此操作的更好方法的问题...
int fib(int n, int val = 1, int prev = 0)
if (n == 0)
return prev;
if (n == 1)
return val;
return fib(n - 1, val + prev, val);
这就是所谓的尾递归,需要 O(n)(实际上它可以通过一个好的编译器进行优化以实现为循环,然后也会消耗恒定的内存消耗)
【讨论】:
请重新格式化。一点都不好看,也不好理解。 我也希望它用于完整的程序,而不仅仅是 fibo(n),因为它位于另一个 for 循环中 还有别的吗?如果您在 tesla GPU 上运行此程序,您是否希望我以卡路里为单位计算功耗? 您应该阅读我要求完整程序的问题。不仅仅适用于 fibo(n)。我从来没有要求过这个 sigma(2^n) 从 1 到 n 是 2^(n+1) - 1 所以总数是 2^11 - 1【参考方案2】:一般来说,它背后有数学,那里解释了斐波那契:https://en.wikipedia.org/wiki/Recurrence_relation
如果您不必证明它而只需要正确地写下来,您只需要考虑算法的行为方式以及某个数字的重复次数,然后您可以尝试将其推广到任何输入n
。
纸是你的朋友!
如果你的递归中有值为“10”的斐波那契,你基本上是在说(10 的斐波那契是 9 的斐波那契 + 8 的斐波那契)
然后你说斐波那契 9 - 它是斐波那契 8 + 斐波那契 7 等等。
你可以画图:
我认为很明显它将继续成为几乎完整的二叉树。您可以看到,对于每个级别,节点数量都翻了一番,因此对于fib(10)
,它将重复自身 10 次,底部几乎是 2^10
,因此对于 fib(n)
,它将是 2^n
。
如何使其在递归算法中有效?好吧,您可以从图片中看到,即 fib(7) 被求解了 3 次。所以你必须记住 fib(n) 一旦你计算了它。它可以是全局变量,也可以通过递归调用传递对对象的引用。
那你不只是说“fib(n-1)和fib(n-2)”,你先看看“fib(n-1)算不算”?如果是这样,请使用计算值而不是递归。
【讨论】:
【参考方案3】:生成斐波那契数列的递归函数生成高度为 n 的二叉树。假设我们取 n=5。那么树形结构会是这样的:
在最底层,我们最终会得到大约 2^n 个节点。因此,时间复杂度将在 O(2^n) 左右,因为递归将针对每个叶节点重复。
我们可以通过使用 memoization 的动态编程方法显着改善这一点,它基本上将重复的子问题(如示例中的 fib(2)
或 fib(3)
)存储在某种查找表中。这将时间复杂度降低到O(n)。
【讨论】:
以上是关于这个递归斐波那契的大 O 时间复杂度?的主要内容,如果未能解决你的问题,请参考以下文章