ACM模板 位運算,(矩陣)快速冪

位運算

  • & 按位與,全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;
    }

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章