我有一个使用递归打印斐波那契数列的程序。有更好的方法,但我被要求使用递归,所以我必须这样做。
以下是该计划:
#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)
我可能也完全错了,但我想要的只是:
这个完整计划的时间复杂度是什么,请简要说明您的计算方法?
如果您有更好的方法使用递归计算Fibonacci,也欢迎。
答案 0 :(得分:5)
c(fibo(n))= c(fibo(n - 1))+ c(fibo(n - 2))+ O(1)
请注意,复杂性遵循精确的公式作为序列,因为所有计算分支总是以值为1的叶结束,因此精确的(theta)复杂度可以通过Fibonacci序列本身的闭合公式精确计算
但这超出了您的问题的范围,我们需要注意的是
c(fibo(n))&lt; 2 * c(fibo(n - 1))
我们现在需要的是解决由
定义的上限序列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)(实际上它可以通过一个好的编译器来优化,实现为一个循环,然后也会耗尽一个恒定的内存消耗)
答案 1 :(得分:4)
总的来说,背后有数学,斐波那契在那里解释:https://en.wikipedia.org/wiki/Recurrence_relation
如果您不必证明它并且您只需要正确地写下来,您只需要考虑算法的行为方式以及某些数字的重复次数,然后您可以尝试将其概括为任何数字。输入n
。
纸是你的朋友!
如果你的递归中你的斐波纳契值为“10”,你基本上就是说(10的finonacci是9 +斐波纳契的斐波那契8)
然后你说斐波那契9 - 它是斐波那契8 +斐波纳契7等。
您可以绘制图表:
我认为很明显它会继续几乎完整的二叉树。并且您可以看到,对于每个级别,节点数量加倍,因此对于fib(10)
,它将重复10次,在底部几乎2^10
,因此对于fib(n)
,它将是2^n
{1}}。
如何使它在递归的alghoritm中有效?那么你可以从图片中看到,即fib(7)被解决了三次。所以你必须在计算后记住fib(n)。它可以是全局变量,也可以通过递归调用传递对象的引用。
然后你不要只说“fib(n-1)和fib(n-2)”,你首先看“是否计算了fib(n-1)”?如果是这样,请使用计算出的值,而不是进行递归。
答案 2 :(得分:0)
用于生成斐波那契数列的递归函数生成高度为 n 的二叉树。假设我们取 n = 5。然后,树形结构将如下所示:
在最底层,我们将得到大约2 ^ n 个节点。因此,时间复杂度将在 O(2 ^ n)左右,因为递归将对每个叶节点重复。
我们可以通过使用备忘录的动态编程方法来显着改善此问题,该备忘录基本上是将重复的子问题(例如示例中的fib(2)
或fib(3)
存储在某种查找表中)。这样可以将时间复杂度降低为 O(n)。