zoukankan      html  css  js  c++  java
  • 斐波拉契数列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次方项了。

  • 相关阅读:
    Mysql 解压安装
    线程进程池,协程,IO模型
    并发编程 线程
    并发编程 进程
    socket 套接字
    网络编程
    面向对象(反射,元类) 排序方法
    面向对象(多态,类方法,魔法方法)
    Day22 面向对象(继承封装)
    php一行代码获取本周一,本周日,上周一,上周日,本月一日,本月最后一日,上月一日,上月最后一日日期 转
  • 原文地址:https://www.cnblogs.com/xcsj/p/12162033.html
Copyright © 2011-2022 走看看