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】。

 

posted @ 2016-07-10 15:09  FuTaimeng  阅读(344)  评论(0编辑  收藏  举报