斐波那契数列 矩阵求法 优化

在做编程题目的时候经常会遇到“斐波那契数列”相关的题目,尤其在做OJ中。下面说一些方法:

  (一)递归

  递归是最慢的会发生重复计算,时间复杂度成指数级。


long long fac(int n)
{
  if(n==1)   return 1;
  else if(n==2)   return 2;
  else    return fac(n-1)+fac(n-2);
}

  (二)循环

  利用临时变量来保存中间的计算过程,加快运算。


long long fac(int n)
{
    long long a=1,b=2,c;
    if(n==1)    return 1;
    else if(n==2)   return 2;
    else
    {
        for(int i=3;i<=n;i++)
        {
            c=a+b;   a=b;   b=c;
        }
    }
    return b;
}


  (三)矩阵乘法+空间换时间(减少乘法,取模运算)

   数列的递推公式为:f(1)=1,f(2)=2,f(n)=f(n-1)+f(n-2)(n>=3)

   用矩阵表示为:

  进一步,可以得出直接推导公式:

   由于矩阵乘法满足结合律,在程序中可以事先给定矩阵的64,32,16,8,4,2,1次方,加快程序的执行时间。(有些题目需要取模运算,也可以事先进行一下)。给定的矩阵次幂,与二进制有关是因为,如下的公式存在解满足Xi={01}: 

 

为了保证解满足 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;
}
 

转自 :旭东的博客

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值