在做编程题目的时候经常会遇到“斐波那契数列”相关的题目,尤其在做OJ中。下面说一些方法:
(一)递归
递归是最慢的会发生重复计算,时间复杂度成指数级。
(二)循环
利用临时变量来保存中间的计算过程,加快运算。
(三)矩阵乘法+空间换时间(减少乘法,取模运算)
数列的递推公式为:f(1)=1,f(2)=2,f(n)=f(n-1)+f(n-2)(n>=3)
用矩阵表示为:
进一步,可以得出直接推导公式:
由于矩阵乘法满足结合律,在程序中可以事先给定矩阵的64,32,16,8,4,2,1次方,加快程序的执行时间。(有些题目需要取模运算,也可以事先进行一下)。给定的矩阵次幂,与二进制有关是因为,如下的公式存在解,满足Xi={0或1}:
为了保证解满足 Xi={0或1},对上述公式的求解从右向左,即求解顺序为Xn,Xn-1,Xn-2,....,X1,X0。
完整代码实现如下:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/**
先用一个数组保存
1 1
1 0
矩阵n次幂的值,这样可以提高运行效率
*/
long long fac_tmp[7][4]=
{
{1,1,1,0},///1 0
{2,1,1,1},///2 1
{5,3,3,2},///4 2
{34,21,21,13},///8 3
{1597,987,987,610},///16 4
{24578,78309,78309,46269},///32 5
{77565,57723,57723,19842} ///64 6
};
int mod=100000;
/**
计算第k个斐波那契数,用来求数组
void get(int k)
{
for(int i=k; i>=0; --i)
{
a=(t00*fac_tmp[0][0]+t01*fac_tmp[0][2])%mod;
b=(t00*fac_tmp[0][1]+t01*fac_tmp[0][3])%mod;
c=(t10*fac_tmp[0][0]+t11*fac_tmp[0][2])%mod;
d=(t10*fac_tmp[0][1]+t11*fac_tmp[0][3])%mod;
t00=a;
t01=b;
t10=c;
t11=d;
}
printf("%d:\n",k+2);
printf("%I64d,%I64d,%I64d,%I64d\n",a,b,c,d);
a=(t00*2+t01*1)%mod;
return a;
}
}
**/
void fac(int k)
{
int i,ok=k;
long long t00=1,t01=1,t10=1,t11=0;
long long a=1,b=1,c=1,d=0;
if(k==1) a=1;
else if(k==2) a=2;
else
{
k=k-3;
if(k>64)
{
for(i=k; i>=64; i-=64)
{
a=(t00*fac_tmp[0][0]+t01*fac_tmp[0][2])%mod;
b=(t00*fac_tmp[0][1]+t01*fac_tmp[0][3])%mod;
c=(t10*fac_tmp[0][0]+t11*fac_tmp[0][2])%mod;
d=(t10*fac_tmp[0][1]+t11*fac_tmp[0][3])%mod;
t00=a;
t01=b;
t10=c;
t11=d;
}
}
i=5;
while(i>=0)
{
if(k>=(long long)pow(2,i))
{
a=(t00*fac_tmp[i][0]+t01*fac_tmp[i][2])%mod;
b=(t00*fac_tmp[i][1]+t01*fac_tmp[i][3])%mod;
c=(t10*fac_tmp[i][0]+t11*fac_tmp[i][2])%mod;
d=(t10*fac_tmp[i][1]+t11*fac_tmp[i][3])%mod;
t00=a;
t01=b;
t10=c;
t11=d;
k=k-(int)pow(2,i);
}
i--;
}
a=(t00*2+t01*1)%mod;
}
printf("Fab(%d) = %I64d\n",ok,a);
}
int main()
{
int k;
while(scanf("%d",&k)!=EOF)
{
fac(k);
printf("\n");
}
return 0;
}
转自 :旭东的博客