您的位置:首页 > 其它

复杂度分析之斐波那契数列

2017-11-01 00:07 169 查看

数列定义

英文名叫Fibonacci sequence,翻译过来就是斐波那契数列,其特点如下:0 1 1 2 3 5 8 ...,简单归纳就是F(0)=0,F(1)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)

函数式

常见的代码表达式采用递归,如下所示

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


时间复杂度

此函数较复杂,无法直接看出,假设n对应复杂度T(n),由于if( n <= 1 )执行1次,f(n-1)执行1次,f(n-2)执行1次,然后f(n-1)和f(n-2)执行一次,因此有T(n)=T(n-1)+T(n-2)+4,忽略次要项,得到T(n)=T(n-1)+T(n-2),根据数学知识可得通项式为



下面继续分析下,不通过数学计算,估算其上下极限

上限:令T(n-2)约等于T(n-1),则T(n)=T(n-1)+T(n-2)=2T(n-1)=2*2*T(n-2)=....=2^n*T(0)=2^n,即为O(2^n)

下限:令T(n-1)约等于T(n-2),则T(n)=T(n-1)+T(n-2)=2T(n-2)=2*2*T(n-4)=....=2^(n/2)*T(0)=2^(n/2),即为O(2^(n/2))

所以最后可得到结果,实际复杂度为图片上x那么多,其结果位于O(2^(n/2))和O(2^n)之间

空间复杂度

跟所有递归调用一样,每次调用一次,都会进行压栈操作,因此下面分析函数调用时栈空间情况,要求f(n)会执行f(n-1)+f(n-2)需要压栈一次,然后求f(n-1)会执行f(n-2)+f(n-3)需要压栈一次,....,最后直到到f(1),这里以f(5)为例,那么栈情况如下图所示



所以总共压栈次数为1+1+..+1=n,复杂度为O(n)

疑问解析

看到这里,当时有个疑问,求f(n)明明会执行f(n-1)和f(n-2)为什么会只压栈一次而不是两次?

为了说明这个问题,特意编写了一段程序,使用gdb调试发现,确实只会压栈一次,每次执行f(n-1)+f(n-2)都只会压栈一次,如下图



看到这里还是挺奇怪的,然后就对程序进行了反汇编objdump -S a.out > tst.dis,发现f函数汇编语句如下所示

int f(int n){
4004ed: 55                   push   %rbp
4004ee: 48 89 e5             mov    %rsp,%rbp
4004f1: 53                   push   %rbx
4004f2: 48 83 ec 18          sub    $0x18,%rsp
4004f6: 89 7d ec             mov    %edi,-0x14(%rbp)
if( n <= 1 ) return n;
4004f9: 83 7d ec 01          cmpl   $0x1,-0x14(%rbp)
4004fd: 7f 05                jg     400504 <f+0x17>
4004ff: 8b 45 ec             mov    -0x14(%rbp),%eax
400502: eb 1e                jmp    400522 <f+0x35>
else return f(n-1)+f(n-2);
400504: 8b 45 ec             mov    -0x14(%rbp),%eax
400507: 83 e8 01             sub    $0x1,%eax
40050a: 89 c7                mov    %eax,%edi
40050c: e8 dc ff ff ff       callq  4004ed <f>
400511: 89 c3                mov    %eax,%ebx
400513: 8b 45 ec             mov    -0x14(%rbp),%eax
400516: 83 e8 02             sub    $0x2,%eax
400519: 89 c7                mov    %eax,%edi
40051b: e8 cd ff ff ff       callq  4004ed <f>
400520: 01 d8                add    %ebx,%eax
}
400522: 48 83 c4 18          add    $0x18,%rsp
400526: 5b                   pop    %rbx
400527: 5d                   pop    %rbp
400528: c3                   retq
很明显可以看到,执行f(n-1)这句话时会会先调用callq  4004ed <f>也即是f(n-2),因此依次操做,会执行压栈从f(n),f(n-1),f(n-2),....f(2),最后执行到f(1),已经能够直接得到结果,因此会返回,根据}可知会弹出栈内容,这时之前压入栈的内容会被依次弹出,最后会执行加法操作,所以综上,执行return f(n-1)+f(n-2)只会压栈一次而不是两次

参考资料

详细分析视频
https://www.youtube.com/watch?v=ncpTxqK35PI https://www.youtube.com/watch?v=pqivnzmSbq4 https://www.youtube.com/watch?v=dxyYP3BSdcQ
参考博客
http://blog.csdn.net/beautyofmath/article/details/48184331
数列通项式求解
https://baike.baidu.com/item/%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97/99145
总结:

斐波那契数列时间复杂度为x所示,介于O(2^(n/2))到O(2^n)之间;空间复杂度为O(n)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: