Fibonacci数列
Fibonacci数是组合数学中非常重要的一个数列,它的递推公式是:
F(1)=F(2)=1
F(n)=F(n-1)+F(n-2)
当然,用这个公式来计算F(n)是非常慢的,当计算F(n)时需要从F(1)一直计算到F(n)。Fibonacci数列还满足一些其他的公式,如:
F(a+b+1)=F(a+1)*F(b+1)+F(a)*F(b)
利用这个公式,可以加速Fibonacci数的计算。我们考虑同时计算F(2n+1)和F(2n),则按照上面的公式:
F(2n+1)=F(n+1)*F(n+1)+F(n)*F(n)
F(2n)=F(n+1)*F(n)+F(n)*F(n-1)=F(n+1)*F(n)+F(n)*(F(n+1)-F(n))
这样,F(2n+1)和F(2n)的计算变为了F(n+1)和F(n)的计算,即下标变为了原来的一半。重复利用这种方法,可以每次让下标变为原来的一半,总共需要大约log n次计算(以2为底)。
当n较大时,后面的方法就比直接的递推要快得多,比如当n=1000000时,后面的方法大概需要20次计算,而直接递推的方法大概需要1000000次计算
Fibonacci的矩阵快速幂算法:
进一步,矩阵乘法满足结合律可以得出直接推导公式:
代码:(codevs 1250 Fibonacci数列)
#include<iostream> #include<cstring> #define Size 5 using namespace std; int n,q; struct Box{ long long data[Size][Size]; int x,y; }; void cheng(Box& a,Box& b,Box& c){ c.x=a.x; c.y=b.y; memset(c.data,0,sizeof(c.data)); for(int i=1;i<=c.x;i++){ for(int j=1;j<=c.y;j++){ for(int k=1;k<=a.y;k++){ c.data[i][j]+=a.data[i][k]*b.data[k][j]; if(c.data[i][j]%=q); } } } } void copy(Box& a,Box& b){ b.x=a.x; b.y=a.y; for(int i=1;i<=a.x;i++){ for(int j=1;j<=a.y;j++){ b.data[i][j]=a.data[i][j]; } } } void out(Box a){ for(int i=1;i<=a.x;i++){ for(int j=1;j<=a.y;j++){ cout<<a.data[i][j]<<' '; } cout<<endl; } } void mi(Box& a,int b,Box& ans){ Box temp; bool flag=false; while(b>0){ if(b&1){ if(flag){ cheng(ans,a,temp); copy(temp,ans); }else copy(a,ans),flag=true; } cheng(a,a,temp); copy(temp,a); b>>=1; } } int main(){ int T; cin>>T; Box a,b,c,d; c.x=2; c.y=1; c.data[1][1]=1; c.data[2][1]=1; while(T--){ cin>>n>>q; n++; if(n==1||n==2){cout<<1<<endl;continue;} a.x=2; a.y=2; a.data[1][1]=1; a.data[1][2]=1; a.data[2][1]=1; a.data[2][2]=0; mi(a,n-2,b); cheng(b,c,d); // cout<<"a:\n";out(a); // cout<<"b:\n";out(b); // cout<<"c:\n";out(c); // cout<<"d:\n";out(d); cout<<d.data[1][1]%q<<endl; } }
codevs 1574 广义斐波那契数列
斐波那契数列的一个变化,基本算法是一样的。
不过a要变成【p,q(endl)1,0】,c变成【a2,a1】。