ACM 位运算,(矩阵)快速幂

你离开我真会死。 提交于 2020-01-11 00:21:12

位运算

  • & 按位与,全1为1。例如5&3-----> 101&11----->1即1
  • | 按位或,有1则1。例如5|3-----> 101|11 ---->111即7
  • ^按位异或,相同为0,不同为1。例如5^3----->101^11---->110即6
  • ~取反运算,0变1,1变0,例如~5—>101—>010即2
  • <<左移指令
  • >>右移指令

快速幂运算,即模平方重复计算法

为什么要有快速幂运算,因为对于c++来说,pow函数在函数库中的定义之中是通过连续相乘得到的结果,那么对于一些小的幂来说,计算确实很快,但是当幂达到1e8往上这些大的幂来说的话,时间复杂度太大,过于耗时,所以采用了快速幂的算法,来提高运算的速度,对于221002^{2^100}来说,普通算法要运算21002^100次,而采用快速幂算法的话,只需要运算100次,也就是时间复杂度是O(logn),原本的时间复杂度是O(n)级别的;

具体原理:

对于想要求得的一个byb^y,我们可以把y化成二级制的科学计数法,也就是y=an2cn+..+a12c1+a0y=a_n2^{c_n}+..+a_12^{c_1}+a_0,然后对于原式来说,就变成了by=ban2cn+..+a12c1+a0=ban2cn...ba12c1ba0b^y=b^{a_n2^{c_n}+..+a_12^{c_1}+a_0}=b^{a_n2^{c_n}}*...*b^{a_12^{c_1}}*b^{a_0}

  • 对于想要求得byb^y
  • 只需要求ban2cn...ba12c1ba0b^{a_n2^{c_n}}*...*b^{a_12^{c_1}}*b^{a_0}
  • 也就是只需要求每个bai2cib^{a_i2^{c_i}},然后对于所有结果相乘即可

怎么求得每一个的结果呢?
我们观察得出,对于二进制的科学计数法来说,aia_i要么是0,要么是1,0、1与cic_i有关,而cic_i又恰好是二进制的位置索引,举个例子:如5,化为二进制101,化为科学计数法:5=122+021+1205=1*2^2+0*2^1+1*2^0
所以对于bai2cib^{a_i2^{c_i}}来说,我们只需要知道y对应的二进制数,就可以知道a与c的每一个值,也就可以知道每一个结果;

代码实现:
#include <iostream>
using namespace std;
#define ll long long //long long 类型,这里相当于把long long定义了一个别名;

//为什么用longlong类型?因为对于一个快速幂来说,运算的结果很有可能是一个大于int类型所能处理的数
//b代表底数,y代表幂
ll QuickPow(ll b, ll y)
{
    //res代表的结果,tmp则是中间变量
    ll res = 1, tmp = b;

    while (y)
    {
        //这个地方也就是,从右往左判断二进制位置上是1还是0,1的话就对于结果进行更新;
        if (y % 2 == 1)
            res *= tmp;
        //tmp代表的就是权
        tmp *= tmp;
        //对于y进行处理,左移一位
        y = (y >> 1);
    }
    //我们最终想要的结果
    return res;
}

int main()
{
    ll a, b;
    cin >> a >> b;
    cout << QuickPow(a, b) << endl;
    return 0;
}

矩阵快速幂:
和上面的差不多,主要是多了一个矩阵乘法的问题;具体看代码吧

#include <iostream>
using namespace std;
#define ll long long //定义别名

//Maxx是矩阵的最大行数,Maxy则是矩阵的最大列数
const int Maxx = 100;
const int Maxy = 100;

//用结构体来表示矩阵
struct matrix
{
   int m[Maxx][Maxy];
};

//矩阵的乘法法则,ax代表a的行数,ay则是a的列数
matrix mul(matrix a, matrix b, int ax, int ay, int by)
{
   //结果矩阵
   matrix res;
   //初始化
   for (int i = 0; i < ax; i++)
      for (int j = 0; j < by; j++)
         res.m[i][j] = 0;

   for (int i = 0; i < ax; i++)
   {
      for (int j = 0; j < by; j++)
      {
         for (int k = 0; k < ay; k++)
         {
            res.m[i][j] = res.m[i][j] + a.m[i][k] * b.m[k][j];
         }
      }
   }
   return res;
}

//b的n次方,lenb代表着b的行数
matrix QuickPow(matrix b, ll n, int lenb)
{
   //res是用来表示结果的
   matrix res;
   //初始化为单位阵
   for (int i = 0; i < lenb; i++)
   {
      for (int j = 0; j < lenb; j++)
      {
         if (i == j)
            res.m[i][j] = 1;
         else
            res.m[i][j] = 0;
      }
   }
   //中间变量
   matrix tmp=b;
   while(n)
   {
      if(n%2==1) res=mul(res,tmp,lenb,lenb,lenb);
      tmp=mul(tmp,tmp,lenb,lenb,lenb);
      n=(n>>1);
   }
   return res;
}

int main()
{
   //用于检验
   matrix x;
    x.m[0][0]=1;
    x.m[0][1]=1;
    x.m[1][0]=1;
    x.m[1][1]=0;
    ll n;
    cin>>n;
    x=QuickPow(x,n,2);
    for (int i = 0; i < 2; i++)
    {
       for (int j = 0; j < 2; j++)
       {
          cout<<x.m[i][j]<<" ";
       }
       cout<<endl;
    }

}

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!