整數快速乘法/快速冪+矩陣快速冪+Strassen算法


快速冪算法可以說是ACM一類競賽中必不可少,並且也是非常基礎的一類算法,鑒於我一直學的比較零散,所以今天用這個帖子總結一下

快速乘法通常有兩類應用:一、整數的運算,計算(a*b) mod c  二、矩陣快速乘法

一、整數運算:(快速乘法、快速冪)

先說明一下基本的數學常識:

(a*b) mod c == ( (a mod c) * (b mod c) ) mod c //這最后一個mod c 是為了保證結果不超過c

對於2進制,2n可用1后接n個0來表示、對於8進制,可用公式 i+3*j == n (其中 0<= i <=2 ),對於16進制,可用 i+4*j==n(0 <= i <=3)來推算,表達形式為2i 后接 j 個0。

 

接下來讓我們盡可能簡單的描述快速乘法的思想:

a*b

快速乘法的基本思想 ,是二進制和乘法分配律的結合,(不由得想起浮點數不滿足結合律,嚴重吐槽!!!╮(╯-╰)╭),比如說,13 ==(1101)2  ,4*13等於4*(1101)2 ,用分配律展開得到4*13 == 4*(1000+100+1)2,我們不難觀察出,快速冪可以通過判斷當前的位(bit)是1還是0,推斷出是否需要做求和操作,每次移動到下一位(bit)時,就對ans進行*2操作,等待是否求和。由於除以2和位移操作是等效的,因此這也可以看作是二分思想的應用,這種算法將b進行二分從而減少了不必要的運算,時間復雜度是log(n)。

 

a^b

快速冪其實可以看作是快速乘法的特例,在快速冪中,我們不再對ans進行*2操作,因為在a^b中b的意義已經從乘數變成了指數,但是我們可以仍然把b寫成二進制,舉例說明:此時,我們將4*13改為4^13,13=(1101)2 ,二進制13寫開我們得到(1000+100+1),注意,這里的所有二進制是指數,指數的相加意味着底數相乘,因此有4^13 == 48 * 44 * 41。再注意到指數之間的2倍關系,我們就可以用很少的幾個變量,完成這一算法。這樣,我們就將原本用循環需要O(n)的算法,改進為O(logN)的算法。

 

按照慣例,給出盡可能簡潔高效的代碼實現 (以下所有int都可用long long 代替

首先,給出快速乘法的實現:

1 //快速乘法 
2 int qmul(int a,int b){// 根據數據范圍可選擇long long 
3     int ans=0;
4     while(b){
5         if( b&1)ans+=a;//按位與完成位數為1的判斷
6         b>>=1;a<<=1;//位運算代替/2和*2
7     }
8     return ans;
9 }

如果涉及到快速乘法取模,則需要進行一些微小改動

改動所基於的數學原理,請參考紅色字體標出的數學常識

1 //快速乘法取模 
2 int qmul_mod(int a,int b,int mod){
3     int ans=0;
4     while(b){
5         if((b%=mod)&1)ans+=a%=mod;//這里需要b%=mod 以及a%=mod 
6         b>>=1;a<<=1;
7     }
8     return ans%mod;  //ans也需要對mod取模 
9 }

 

接下來是快速冪的實現:

 1 //快速冪 a^b 
 2 int qpow(int a,int b){
 3     if(a==0)return 0;//這是個坑,校賽被坑過,很多網上的實現都沒寫這一點
 4     int ans=1;
 5     while(b){
 6         if(b&1)ans*=a;//和快速乘法的區別
 7         b>>=1;a*=a;//區別,同上
 8     }
 9     return ans;
10 } 

以及含有取模的快速冪:

int qpow_mod(int a,int b,int mod){
    if(a==0)return 0;
    int ans=1;
    while(b){
        if(b&1)ans=(ans%mod)*(a%mod);//如果確定數據不會爆的話,可寫成 ans*=a%=mod;
        b>>=1;a*=a%=mod;//等價於a=(a%mod)*(a%mod),且將一個模運算通過賦值代替,提高了效率
    }
    return ans%mod;//數據不會爆的話,這里的%運算會等價於第5中不斷重復的 ans%mod
}

 

如果我們對於性能還有更進一步的要求,那么也就是減少取模運算了,那么我們需要確定數據范圍不會爆掉

在這樣的前提下,我們可以只用原先1/4的取模運算量完成快速冪

int qpow_mod(int a,int b,int mod){
    if(!a)return 0;
    int ans=1;
    while(b){
        if(b&1)ans*=a%=mod;//這里的模運算只有一個
        b>>=1;a*=a;//這里的模運算沒有了
    }
    return ans%mod;
}

這些天找了好久,終於找到了純粹的整數快速冪題目,按照慣例,給一波傳送門:

poj1995:http://poj.org/problem?id=1995

 

這個題。。。沒什么好說的,但是需要注意,用1/4模運算量的那種寫法,數據會爆,所以必須寫成完全取模的運算,這樣程序會慢一點。。。嗚嗚嗚,63ms水過,這是目前我做的最慢的了,如果大神知道如何在16ms及以下A掉它,歡迎聯系我謝謝~o(* ̄▽ ̄*)ブ

實現的代碼如下:

 1 #include<cstdio>
 2 int z,a,b,m,h,sum;
 3 int qpow_mod(int a,int b,int mod){
 4     if(!a)return 0;
 5     int ans=1;
 6     while(b){
 7         if(b&1)ans=ans%mod*(a%=mod);
 8         b>>=1;a=a%mod*(a%mod);
 9     }
10     return ans%mod;
11 }
12 int main(){
13     scanf("%d",&z);
14     while(z--){
15         scanf("%d%d",&m,&h);sum=0;
16         while(h--){
17             scanf("%d%d",&a,&b);
18             sum+=qpow_mod(a,b,m);
19             sum%=m;
20         }
21         printf("%d\n",sum);
22     }
23 }
View Code

 

先更新到這,有時間再更新矩陣的Strassen算法以及矩陣快速冪,,大家稍后見(●'◡'●)

2016-06-13 16:47:56

大家好,我又回來啦

二、矩陣運算:(快速冪)(Strassen算法有空再說)

矩陣的快速冪運算,其實思路和上面的整數快速冪是一樣的,對指數進行二分,不過我們對於快速冪本身,可能既可以寫成函數,也可以寫成運算符重載,所以這里我寫的是運算符的重載,畢竟重載練得少,得多練一練

首先我們可以定義一個矩陣數據結構,也可以直接用二維數組

1 #define N 100
2 struct matrix{
3     int m[N][N];
4 };

然后我們重載^運算符,完成矩陣m的b次冪的快速冪運算

這里為了我自己代碼習慣,我重載了*和*=兩種運算符,當然,在寫的時候跪在了忘了寫函數聲明上,畢竟C++不是java,對於函數聲明的順序有依賴,so~大家記得寫函數聲明呦

代碼如下

 1 //矩陣的數據結構
 2 struct matrix{
 3     int m[N][N];
 4 };
 5 matrix operator * (matrix ,matrix);//重載聲明
 6 matrix operator *= (matrix,matrix);
 7 matrix operator ^ (matrix a,int b){
 8     matrix ans;
 9     for(int i=0;i<N;i++)
10         for(int j=0;j<N;j++)ans.m[i][j]=(i==j);//初始化為單位矩陣 
11     if(b&1)ans*=a;
12     b>>=1;a*=a;
13     return ans;
14 }
15 matrix operator * (const matrix a,const matrix b){//朴素矩陣乘法
16     matrix ans;
17     for(int i=0;i<N;i++)
18         for(int j=0;j<N;j++)
19             for(int k=0;k<N;k++)
20                 ans.m[i][j]=a.m[i][k]+b.m[k][j];
21     return ans;
22 }
23 matrix operator *= (matrix a,const matrix b){
24     return a=b*b;
25 }

當然,有必要的時候,我會再更新一波Strassen算法,考慮到在很多情況下,Strassen算法反而會降低矩陣運算的速度,所以我們就先到這里~拜拜(●ˇ∀ˇ●)

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM