斐波拉契数列2的63次方项

              首先,斐波拉契数列最简单的做法就是利用递推公式。这里就不写代码了。

              但是递推计算还是太慢了,项数一大就算不了,比如10的5次方以上就算不出来了。

              然后我们就想出了矩阵加速。

              利用递推关系可以构造成矩阵相乘。

              |  a(n+1)  an  |  =  |  an  a(n-1)  |  *  |  1  1  |

              |  0   0    |      |  0    0    |      |  1  0  |

              则可以得到

              |  a(n+1)  an  |  =  |  a2   a1    |  * ( |  1  1  | )^(n-1)

              |  0   0    |      |  0    0    |       |  1  0  |

              再利用快速幂的思想,就有了下面的代码。

        #include<stdio.h>
        #include<math.h>
        #include<stdlib.h>
        struct jz
        {
         unsigned long long at[2][2];
        };
        jz ttt,jsjz;
        jz mutil(jz p,jz q)            //自定义的矩阵乘法
        {
         jz pq;
         pq.at[0][0]=(p.at[0][0]*q.at[0][0]+p.at[0][1]*q.at[1][0])%1000000007;
         pq.at[0][1]=(p.at[0][0]*q.at[0][1]+p.at[0][1]*q.at[1][1])%1000000007;
         pq.at[1][0]=(p.at[1][0]*q.at[0][0]+p.at[1][1]*q.at[1][0])%1000000007;
         pq.at[1][1]=(p.at[1][0]*q.at[0][1]+p.at[1][1]*q.at[1][1])%1000000007;
         return pq;
        }
        jz jiasu(unsigned long long a)                  //加速幂的思想
        {
         if(a==1) return jsjz;
         else if(a%2==0) return mutil(jiasu(a/2),jiasu(a/2));
         else return mutil(mutil(jiasu(a/2),jiasu(a/2)),jsjz);
         }
        int main()
        {
         jsjz.at[0][0]=2;ttt.at[0][0]=1;
         jsjz.at[0][1]=1;ttt.at[0][1]=1;
         jsjz.at[1][0]=1;ttt.at[1][0]=1;
         jsjz.at[1][1]=1;ttt.at[1][1]=0;
         unsigned long long n;
         scanf("%llu",&n);
         jz p;
         if(n==1) p=ttt;
         else if(n%2==0) p=jiasu(n/2);
         else p=mutil(ttt,jiasu(n/2));
         printf("%llu",p.at[0][1]);
         return 0;
         }

               但是这样还是达不到2的63次方的要求。

               我们再想一想还有什么可以优化的地方。

                  很明显我们在加速幂的运算中还是重复了很多运算。我们现在就需要想有没有什么办法去除这些重复的运算。

               这里就用到了另一种加速幂的思路。

        #include<stdio.h>
        #include<math.h>
        #include<stdlib.h>
        struct jz
        {
         unsigned long long at[2][2];
        };
        jz ttt,jsjz;
        jz mutil(jz p,jz q)
        {
         jz pq;
         pq.at[0][0]=(p.at[0][0]*q.at[0][0]+p.at[0][1]*q.at[1][0])%1000000007;
         pq.at[0][1]=(p.at[0][0]*q.at[0][1]+p.at[0][1]*q.at[1][1])%1000000007;
         pq.at[1][0]=(p.at[1][0]*q.at[0][0]+p.at[1][1]*q.at[1][0])%1000000007;
         pq.at[1][1]=(p.at[1][0]*q.at[0][1]+p.at[1][1]*q.at[1][1])%1000000007;
         return pq;
        }
        /*jz jiasu(unsigned long long a)
        {
         if(a==1) return jsjz;
         else if(a%2==0) return mutil(jiasu(a/2),jiasu(a/2));
         else return mutil(mutil(jiasu(a/2),jiasu(a/2)),jsjz);
         }*/
        int main()
        {
         jsjz.at[0][0]=1;ttt.at[0][0]=1;
         jsjz.at[0][1]=1;ttt.at[0][1]=0;
         jsjz.at[1][0]=1;ttt.at[1][0]=0;
         jsjz.at[1][1]=0;ttt.at[1][1]=1;
         unsigned long long n;
         scanf("%llu",&n);
         for(int i=0;i<=63;i++)        //加速幂思想
         {
          if(i>=1) jsjz=mutil(jsjz,jsjz);
          if((n>>i)&1==1) ttt=mutil(ttt,jsjz);
         }
         printf("%llu",ttt.at[0][1]);
         return 0;
        }

               一个数总可以表示成多个2的次方项相加,也就是2进制表示。于是就把次方项给拆开,只求对应的需要的项。

               其中可以利用加速矩阵的自乘,免去所有的重复操作。这也就是比第一次的加速幂思想更简化的地方。

                 于是现在复杂度就是肉眼可见的低了。

               只需要进行一百多次的矩阵乘法。

               现在就可以求出斐波拉契数列的2的63次方项了。

posted @ 2020-01-07 15:56  Xcsj  阅读(573)  评论(1编辑  收藏  举报