1. 简述
斐波那契数列的递归公式为:f(0)=0,f(1)=1,f(n)=f(n-1)+f(n-2), n>1。尽可能快的计算f(n)。
2. 思路
一个就是公式解,算特征方程的方法,好像是线性代数部分的,有点忘了,这里直接给答案吧:根据递归公式,f(n)=f(n-1)+f(n-2),知道f(n)的特征方程为 x*x = x + 1,根为s1=(1+sqrt(5))/2 和s2=(1-sqrt(5))/2。那么存在A,B使得:f(n)=A*s1^n + B*s2^n,代人f(0)=0,f(1)=1,得到A=sqrt(5)/5,B=-sqrt(5)/5。即f(n)=A*s1 + B*s2。
另一个就是递归解法,依次来求,O(n)的复杂度。
还有一个就是在递归解法中,引入矩阵运算:(f(n), f(n-1)) = (f(n-1), f(n-2)) * A,A(0,0)=1, A(1,0)=1,A(0,1)=1,A(1,1)=0。 递推过去:(f(n), f(n-1)) = (f(1), f(0)) * A^(n-1),对于A^(n-1)的计算是可以保证在log(n-1)级别的。以为n-1的二进制表示有log(n-1)位,计算每个二进制位的数值,然后对于是1的二进制位上的数值相加即可。比如n-1=5,需要计算的就是A^1,A^2,A^4, 而A^5 = A^1 + A^4。(5的二进制位101)。
3. 矩阵怎么算
上面A的矩阵是有规律的 (f(n), f(n-1)) = (f(n-1), f(n-2))*A,按照列来看比较容易,A的第一列可以把f(n)搞出来,因此第一列为(1,1)即可,第二列把f(n-1)搞出来即可,因此第二列定为(1,0)。对于扩展中的f(k)=f(k-1)+f(k-2)+f(k-3),(f(k),f(k-1),f(k-2)) = (f(k-1),f(k-2),f(k-3))*A,第一列搞出来f(k),因此第一列为(1,1,1),第二列搞出来f(k-1),因此第二列定义为(1,0,0),第三列搞出来f(k-2),因此定义为(0,1,0)。
4. 对于扩展中求余
当n很大时,比如n=2^60,如何计算f(n) mod M (M<100000)呢?
好像见过M比较小的情况,比如m=5的时候,一般可以多推几个数字,后面会出现循环节的,然后根据n和循环节的长度,就能把f(n)对应到f(k)上,k是一个很小的数字。对于M比较大的话,应该也可以用这个方法,不过就是要编程实现循环节的判断了,一般来说对于f(n)=f(n-1)+f(n-2),依次可以计算f(0),f(1),f(2),...当f(i)=f(0)且f(i+1)=f(1)时,循环节就找到了,即长度为i,f(2^60) = f(2^60 mod i) = f(1024^6 mod i) = f( (1024 mod i)^6 mod i)。直接算2^60可能会溢出,__int64还刚好,不过如果更大__int64也不够。
4. 参考
编程之美,2.9节,斐波那契数列