A = 1, 1, 2, 3, 5, 8, 13, 21, ...
问题:怎么求出序列中第N个数?
计算如上算法f(n)=f(n-2)+f(n-1)计算斐波那契数需要的时间复杂度和空间复杂度
fib(1)=1,fib(2)=1,n=8时可以画出如下递归树表示fib(8)计算该算法的时间复杂度
计算fib(8)需要先计算fib(7)+fib(6),以此类推,可以得到以上递归树图
递归树第1层,已知fib(6),fib(7),计算fib(8)需要1个加法操作(fib(8)=fib(6)+fib(7)),
递归树第2层,已知fib(4),fib(5),计算fib(6),fib(7)需要2个加法操作(fib(7)=fib(5)+fib(6)); fib(6)=fib(4)+fib(5))),
递归树第3层,已知fib(2),fib(3),fib(4),fib(5),计算fib(4),fib(5),fib(5),fib(6)需要4个加法操作
递归树第4层,需要8个加法操作
........
所以fib(8)=个操作,使用等比数列求和公司计算结果为,函数和n的关系为 ,所以可以得的时间复杂度为
递归树的高度也是随着n呈线性增长的,所以
空间复杂度
内存中存储fib(8)...fib(1),fib(1)=1,fib(2)=1,计算出fib(3)=2,所以fib(3)出栈,计算fib(4),需要先计算fib(3),fib(1)和fib(2)入栈,得出fib(3),进而计算出fib(4),然后fib(4)出栈,所以内存中只需要存储8个变量,空间复杂度和n线性相关,空间复杂度为O(n)
相关涉及点补充
等比数列的求和公式
a1为等比数列的首项,n为项数,q为公比
优化上述斐波那契数列算法
fib(n)=fib(n-2)+fib(n-1)如上图递归树所示,计算顶层fib(8)每次都要重复去再次计算fib(6),fib(7),可以想到的优化办法是将之前fib(6),fib(7)的计算结果存储起来,然后直接获取计算好的fib(6),fib(7)对应的数值,返回结果。
简化版本
维护一个fib(1)fib(2)fib(3)fib(4)...fib(n)数组,如果要计算fib(n)则只需要直接从数组中取出对应的fib(n-2)fib(n-1)计算好的结果,然后相加返回结果即可,该种思路为动态规划(Dynamic Programing)DP算法(核心是维护一个数组把已经计算好的结果填入数组,后面直接取用数组中的数据,而不需要重复计算)
改进算法如下:
此时算法时间复杂度为O(n) 空间复杂度为O(n)
如上算法降低了时间复杂度,但是空间上存在浪费,只需要存储前两个的计算结果,而不需要将所有之前的计算结果都存储在数组中,所以还可以做如下简化:
此算法空间复杂度为O(1)时间复杂度不变,依然为O(n)
思考题:怎么在O(1)的时间复杂度下计算fib(n)?
f(n)=closed-form solution
斐波那契通项公式法:
套公式计算
import math def fib(n): sqrtfive = math.sqrt(5) return sqrtfive / 5 * (math.pow((1 + sqrtfive) / 2, n) - math.pow((1 - sqrtfive) / 2, n))
斐波那契通项公式怎么得来的?
提示:换成矩阵的连乘形式,矩阵连乘可以简化(matrix decomposition)