1. 程式人生 > >快速冪取模和快乘取模

快速冪取模和快乘取模

要去 ont pow 取模 當下 tex str 過程 return

一、快速冪取模概念

  快速冪取模,顧名思義,就是快速的求一個冪式的模(余),比如a^b%c,快速的計算出這個式子的值。

  在程序設計過程中,經常要去求一些大數對於某個數的余數,為了得到更快、計算範圍更大的算法,產生了快速冪取模算法。

二、快速冪取模算法實現

 1)很容易能想到,循環b次,每次乘a,最後對c取余就可以了。

int ans = 1;
for(int i = 1; i<=b; i++)
{
    ans = ans * a;
}
ans = ans % c;

  這個樸素算法的問題是:

    1.如果a和b都很大,那麽ans很有可能超出long long類型,最後結果錯誤

    2.如果b很大,這樣計算會超時。

  所以對這個算法需要改進

 2)有一個很簡單的公式如下:

   技術分享

  證明略,由上面公式我們可以對上面算法進行優化

int ans = 1;
a = a % c;
for(int i = 1; i<=b; i++)
{
    ans = (ans * a) % c;//這裏再取了一次余
}
ans = ans % c;

  這樣就解決了超出long long的問題了,但是時間復雜度還沒有降低。

 3)為了解決超時問題,我們可以使用下面的公式,證明略

    技術分享

int ans = 1;
a = a % c;
if(b%2==1)
    ans 
= (ans * a) mod c; //如果是奇數,要多求一步,可以提前算到ans中 k = (a*a) % c; //我們取a2而不是a for(int i = 1; i<=b/2; i++)    { ans = (ans * k) % c; } ans = ans % c;

  這樣,我們就把時間復雜度降到b/2了,觀察上式子,可以發現,當我們令k = (a * a) mod c時,狀態已經發生了變化,我們所要求的最終結果即為(k)^b/2 mod c而不是原來的a^b mod c,當下次求(k)^b/2 mod c時,我們可以繼續對這個式子記成上面格式,我們發現這個過程是可以重復下去的。

 4)最終我們得到快速冪取模的算法

int ans = 1;
a = a % c;
while(b>0) 
{
    if(b % 2 == 1)
        ans = (ans * a) % c;
    b = b/2;
    a = (a * a) % c; 
}

三、快速冪取模模板

int PowerMod(int a, int b, int c)//a^b%c
{
    int ans = 1;
    a = a % c;
    while(b>0)
    {
        if(b % 2 = = 1)
            ans = (ans * a) % c;
        b = b/2;
        a = (a * a) % c;
            
    }
    return ans;
      
}

四、快乘取模

 1.概念:

  快速乘法是求兩個數相乘,即求解a*b%c,很明顯這個不會超時,但是如果當a和b特別大的時候,兩個數相乘可能超過long long 範圍,所以要使用快速乘法,因為在加法運算的時候不會超,而且可以直接取模,這樣就會保證數據超不了了。快速乘法的思想和快速冪的思想一樣,快速冪是求一個數的高次冪,快速乘法是求兩個數相乘。

 2.算法實現:

  這樣的式子和a^b%c很像,所以可以用類似於快速冪取模的方法來做。即,將b寫成二進制來看,然後拆開相加(正因為二進制的特殊性,才有快速乘和快速冪的成功),比如下面的例子:

 技術分享

技術分享

32+16+4=52 (實際操作過程中,每次相加都取模)

這一過程和快速冪取模非常相似。

 3.模板:

int multiMod(int a, int b, int c)
{
    int ans = 0;//註意初始化是0,不是1
    while (b)
    {
        if (b & 1)
            ans=(ans+a)%c;
        a = (a + a) % c;//和快速冪一樣,只不過這裏是加
        b >>= 1;
    }
    return ans; 
}

快速冪取模和快乘取模