1. 程式人生 > >整數快速乘法/快速冪+矩陣快速冪

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

快速乘法通常有兩類應用:一、整數的運算,計算(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 代替)

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

int qmul(int a, int b){//根據資料範圍可選擇long long 
    int ans=0;
    while(b){
        if(b&1)
            ans+=a;//按位與完成位數為1的判斷
        b>>=1;
        a<<=1;//位運算代替/2和*2
    }
    return ans;
}

快速乘法取模:

int qmul_mod(int a, int b,int mod){//根據資料範圍可選擇long long 
    int ans=0;
    while
(b){ if((b&1) ans=(ans+a)%mod;//這裡需要b%=mod 以及a%=mod b>>=1; a<<=1;//位運算代替/2和*2 } return ans;// }

接下來是快速冪的實現:

//快速冪 a^n 
int qpow(int a, int n){//根據資料範圍可選擇long long 
    if(a==0)return 0;//這是個坑,校賽被坑過,很多網上的實現都沒寫這一點
    int ans=1;
    while(n){
        if(n&1)
            ans*=x;//和快速乘法的區別
    x*=x;//區別,同上
        n>>=1;

    }
    return ans;
}

以及含有取模的快速冪:


int qpow_mod(int a, int n,int mode){//根據資料範圍可選擇long long 
    if(a==0)
        return 0;//這是個坑,校賽被坑過,很多網上的實現都沒寫這一點
    int ans=1;
    while(n){
        if(n&1)
            ans=(ans*x)%mod;
        x=(x*x)%mod;
        n>>=1;

    }
    return ans;
}

矩陣乘法:

int c[N][N];  
void multi(int a[][N],int b[][N],int n)  
{  
    memset(c,0,sizeof c);  
    for(int i=1;i<=n;i++)  
        for(int k=1;k<=n;k++)  
        for(int j=1;j<=n;j++)  
        c[i][j]+=a[i][k]*b[k][j];  
} 

矩陣快速冪:
就是算A^n;方法很簡單,把快速冪演算法中的乘法改成矩陣的乘法就可以了

    const int N=10;  
    int tmp[N][N];  
    void multi(int a[][N],int b[][N],int n)  
    {  
        memset(tmp,0,sizeof tmp);  
        for(int i=1;i<=n;i++)  
            for(int j=1;j<=n;j++)  
            for(int k=1;k<=n;k++)  
            tmp[i][j]+=a[i][k]*b[k][j];  
        for(int i=1;i<=n;i++)  
            for(int j=1;j<=n;j++)  
            a[i][j]=tmp[i][j];  
    }  
    int res[N][N];  
    void Pow(int a[][N],int n)  
    {  
        memset(res,0,sizeof res);  
        for(int i=1;i<=n;i++) res[i][i]=1;  
        while(n)  
        {  
            if(n&1)  
                multi(res,a,n);//res=res*a;複製直接在multi裡面實現了;  
            multi(a,a,n);//a=a*a  
            n>>=1;  
        }  
    }