斐波拉契数列加强版——时间复杂度O(1),空间复杂度O(1)
对于斐波拉契经典问题,我们都非常熟悉,通过递推公式F(n) = F(n - 1) + F(n - 2),我们可以在线性时间内求出第n项F(n),现在考虑斐波拉契的加强版,我们要求的项数n的范围为int范围内的非负整数,请设计一个高效算法,计算第n项F(n)。第一个斐波拉契数为F(0) = 1。 给定一个非负整数,请返回斐波拉契数列的第n项,为了防止溢出,请将结果Mod 1000000007。
斐波拉契数列的计算是一个非常经典的问题,对于小规模的n,很容易用递归的方式来获取,对于稍微大一点的n,为了避免递归调用的开销,可以用动态规划的思想轻松获得,时间复杂度为O(n),空间复杂度为O(1). 但是对于更大规模,比如上题中的n的范围,动态规划所花的时间也不少了。好在之前在Code Hunt上碰倒这个问题。
考虑下面三个矩阵:
F(n) F(n-1) 1,1 F(n-1) F(n-2)
F(n-1) F(n-1) 与 1,0 与 F(n-2) F(n-3)
分别设为S(n), M, S(n-1). S(n) = M x S(n-1).
于是
S(n) = M^(n-1) x S(1) = M^n; (关系1)
再来看看,若n为32位正整数,设c[32]为其二进制序列的逆序列, c[i] =0,1;
n = Σ(c[i]*2^i), i=0,...,31;
所以只要我知道M的2^i 方幂(i=0,1,2,...,31),我们可以轻松计算出S(n)
而计算出32个M的幂需要做32次矩阵乘法,而计算M^n次方,即M^( Σ(c[i]*2^i) ) 也最多需要做32次矩阵乘法
因而最多64次矩阵乘法即可计算出任意32位正整数范围的n对应的S(n)
1 class Fibonacci {
2 /*author: Haibin Chen*/ 3 public: 4 int getNthNumber(int n) { 5 // write code here 6 if(n <=1) return 1; 7 long a[32]; 8 long b[32]; 9 long c[32]; 10 long d[32]; 11 a[0]=1; 12 b[0]=1; 13 c[0]=1; 14 d[0]=0; 15 int i,j; 16 for(i=1;i<32;i++){ 17 a[i] = a[i-1]; 18 b[i] = b[i-1]; 19 c[i] = c[i-1]; 20 d[i] = d[i-1]; 21 getMulti(a[i],b[i],c[i],d[i],a[i],b[i],c[i],d[i]); 22 } 23 long ra = 1,rb =0,rc=0,rd=1,mask=1; 24 for(i=0;i<32;i++){ 25 if((n&mask)!=0){ 26 getMulti(ra,rb,rc,rd,a[i],b[i],c[i],d[i]); 27 } 28 mask = mask <<1; 29 } 30 return ra; 31 } 32 void getMulti(long &a,long &b,long &c,long &d,long a1,long b1,long c1,long d1){ 33 long tempa = (a*a1+b*c1)%1000000007; 34 long tempb = (a*b1+b*d1)%1000000007; 35 long tempc = (c*a1+d*c1)%1000000007; 36 long tempd = (c*b1+d*d1)%1000000007; 37 a = tempa; 38 b = tempb; 39 c = tempc; 40 d = tempd; 41 } 42 };
Bingo,go,go,go!