c++--求斐波拉切数列最快的方法(矩阵快速幂方法)O(n)=logn(详细)

求斐波拉切数列常用方法:

递归法:O(n)=n*n

for循环法:O(n)=n

矩阵快速幂法:O(n)=logn

先介绍数快速幂法

求一个数的n次方,如果n很大,比如:

 int n;
  int a;
  cin>>a;
  cin>>n;
  pow(a,n);
  //n>100000000
 

这时如果再用头文件#include<cmath>中的pow函数会非常慢,所以不可取。

如果用for循环方法,比如2^11,用for循环要计算11次,但是数字11可以化为二进制1011,所以也等于2^(1011),同时,(1011)等于2^0+2^1+2^3,那么2^11也等于2^(2^0+2^1+2^3),将式子拆成乘法形式:2^11=2^(2的0次方)+2^(2的一次方)+2^(2的3次方),可以发现,这三个数字后一个都是前一个的平方。这就是代码中的:a=a*a;(代码在下文)

同时,如果要我们计算3^10,我们该如何计算,我们首先想到的流程就是这样:

3^10---->9^5---->9*9^4---->9乘以(81)^2---->9乘以(81^2)^1,

其实这也利用了快速幂的方法:

先定义一个变量ans来存储最终结果

如果指数是偶数,将底数平方、再将指数除以二。

如果指数是奇数,先将ans*底数,然后指数就减少了1,就可以按照上一步的偶数进行操作了,直到指数为0

具体代码如下:

 #include<iostream>
  ​
  using namespace std;
  ​
  typedef long long ll;//因为数字很大,要用long long存储
  ll pow(ll a,ll n)//a是底数,n是指数
  {
      ll res; //存储最后的结果
      while(n)//指数为0停止循环
      {
          if(n&1)//这个是什么意思,文章最后会说
              res=res*a;
          a=a*a;
          n=n>>1;//二进制中这就相当于除以2
      }
      retur res;
  }

再介绍矩阵快速幂法

基础知识:矩阵的乘法运算。此处不再多言。

注意:矩阵乘以数字叫做矩阵的数乘,矩阵乘以矩阵才叫矩阵的乘法运算

在斐波那契数列之中

f[i] = 1f[i-1]+1f[i-2] f[i-1] = 1f[i-1] + 0f[i-2];

所以

就这两幅图完美诠释了斐波那契数列如何用矩阵来实现。

与数快速幂法的区别就是用矩阵代替数字进行操作运算,实际核心思想没有任何变化。具体代码如下:

#include<iostream>
  #include<vector>
  ​
  using namespace std;
  ​
  typedef long long ll;
  typedef vector<ll> vec;
  typedef vector<vec> mat;//这里是vector容器嵌套,数组嵌套数组,用作二维数组。mat是矩阵常用词语
  ​
  //矩阵相乘算法(需要重载运算符,因为*起初的定义是数字与数字之间的,现在我们要用于矩阵与矩阵)
  mat operator * (mat a,mat b)
  {
      mat c(2,vec(2));//定义两行两列数组,因为用到的数组是已经确定的,所以可以直接定义两行两列数组
      for(int i=0;i<2;i++)
          for(int k=0;k<2;k++)
              for(int j=0;j<2;j++)
                  c[i][j]+=a[i][k]+b[k][j];
      return c;
  }
  //矩阵快速幂算法
  mat pw(mat a, ll n)
  {
      mat c(a.size(),vec(a.size()));
      for(int i=0;i<a.size();i++)
      {
          c[i][i]=1;//初始化矩阵,相当于在数字中给一个变量初始化为1一样
      }
      while(n)
      {
          if(n)
              c=c*a;
          a=a*a;
          n=n>>1;
      }
      return c;
  }
  //主函数进行初始准备
  int main()
  {
      ll n;
      cin>>n;
      mat a(2,vec(2));//创建一个初始化数组作为累乘数组
      a[0][0]=1;
      a[0][1]=1;
      a[1][0]=1;
      a[1][1]=0;
      a=pow(a,n);
      cout<<a[0][1];
      return 0;
  }

n&1与1&n

n&1判断n的二进制表示末尾是不是1(判断n是不是奇数),是的话返回1,不是的话返回0

1&n检查n的二进制的最低为,如果是1就返回1,否则就返回0

注意事项

  1. (n=n>>1)!=(n>>1)这两个式子不一样,n>>1的返回值是一个新的数字,要用原来的变量来接受才可以改变原先变量的值。

  2. 矩阵也要初始化成为单位矩阵,即对角线上(左上角到右下角)的数字全部为1,其他都是0,不初始化的话,最终结果会一直是0.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值