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

来源:互联网 发布:2017nba总决赛欧文数据 编辑:程序博客网 时间:2024/06/18 13:43

数列定义

英文名叫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)

原创粉丝点击