Ox00 结论
个人认为,严格意义上的时间复杂度应为:
0x01 引言
最近学习数据结构,书中将递归实现的Fibonacci数列的时间复杂度一笔带过,称Fibonacci数列的时间复杂度为O(2^n),在网上查了查O(2^n)的由来。但在知乎上又看到有人将Fibonacci数列的时间复杂度算出来是 。这里进行讨论。个人想法,有问题希望大家多多包含,并指出问题所在。
0x02 递归代码
- C代码
#include <stdio.h>
long fib(int n)
{
if(n==0 || n==1)
return 1;
return fib(n-1) + fib(n-2);
}
int main()
{
int i;
for(i=0; i<10; i++)
{
printf("%d\n", fib(i));
}
return 0;
}
复制代码
- 运行
0x03 时间复杂度
- 说法一
如上图,要想得到
fib(6)
需要先计算fib(5)
和fib(4)
; 想要的到fib(5)
需要先计算fib(4)
和fib(3)
;以此类推。将红色框内的fib(2)
和fib(1)
移动到上一次最右侧,则上一层共 。这样作为计算,很容易得到时间复杂度为 。
- 说法二
fib(1)
和fib(2)
的个数并不能直接用 进行粗略的计算。记fib(6)
为第1层,则fib(1)
为第6层,fib(2)
为第5层。显然,fib(1)
和fib(3)
的个数是相同的。所以fib(1)
和fib(2)
的总个数为fib(4)+fib(5)=3+5=8
。而又fib(4)+fib(5)=fib(6)=fib(n)
,所以fib(1)
和fib(2)
的总个数为fib(n)
。根据斐波那契数列的通项公式得出:通过求同阶方程,最终得出时间复杂度为 。
- 分析(求解)
-
f(n)
fib(n)
的语句频度等于递归过程中fib(1)
和fib(2)
总的执行次数。所以,求解递归算法的时间复杂度相当于递归方程求解。
对于斐波那契数列
当
n>=2时
有f(n)=f(n-1)+f(n-2)
当
n=0
或n=1
有f(n)=1
递归方程求解,也就是求斐波那契数列的通项公式。
-
"O"
求与
Fn
同阶的函数。算法中同阶指的是,当n->∞
时,两个函数之比的结果是一个非0的常数,则称这两个函数是同阶的。-
如果时间复杂度是 。相比求极限,结果为
0
-
如果时间复杂度是 。相比求极限,结果为
-
-
ret: 斐波那契数列的通向公式函数与 是同阶的。
- 另一种思路
设
fib(n)
的执行时间为T(n)
, 则fib(n-1)
的执行时间为T(n-1)
,fib(n-2)
的执行时间为T(n-2)
。语句 if(n==0 || n==1) return 1; 的执行时间为O(1)
。if
判断语句相对于return
语句的时间可以忽略。所以 。
当
n=1
或n=2
时,fib
函数执行return 1
,执行时间为 。综上:
递归方程求解,得出
fib(n)
执行时间为:
当
n->∞
再化简,可以得到时间复杂度为 。
0x04 个人想法
其中数学符号“O”的严格定义为:
若T(n)和f(n)是定义在正整数集合上的两个函数,则T(n)=O(f(n))表示存在正的常数C和n0,使得当n>=n0时都满足0<=T(n)<=Cf(n)。
所以,从严格意义上来讲, 也是不对的。
其中 的正负与n的奇偶相关,所以f(n)是波动的,不可能存在C,使得当n>=n0时都满足0<=T(n)<=Cf(n)。
综上,严格意义上,时间复杂度应为:
0x05 总结
通过分析(非严格意义上),斐波那契数列递归形式的时间复杂度更应该为 。
那 又是怎么回事?
-
首先,前面举例的
fib(6)
通过 算出来的结果和通过 算出来的结果相同,纯属偶合。当n不为6时,显然是不相同的,方程都不同。 -
两个时间复杂度的共同点是,都是指数阶。因为 是指数阶的代表,所以使用 作为递归求解斐波那契数列的时间复杂度,也可能只是想表明它是指数阶。
-
一般指数阶的算法根本不考虑使用,所以知道这是个指数阶的时间复杂就ok了,研究它的时间复杂度具体是什么也是没有必要的。并且(渐进)时间复杂度本来就不是机器执行算法的真实时间。
两个时间复杂度的函数图像