1. 程式人生 > >快速冪取餘演算法思路詳解

快速冪取餘演算法思路詳解

【概述】
計算xy % n;如果採用常規方法,當x與y都比較小的情況下,採用直接計算可以,但是如果當x跟y都非常大的時候,如21000 % 100000,那該如何解決呢?
【離散數學有關餘數知識點補充】
重視餘數的性質:
1. (a+b)%m == (a%m+b%m)%m
2. a*b%c=((a%c)*b)%c
3. ab%c=(a%c)b%c

下面是講解快速冪:(By 夜せ︱深 感謝作者)

快速冪取模演算法

在網站上一直沒有找到有關於快速冪演算法的一個詳細的描述和解釋,這裡,我給出快速冪演算法的完整解釋,用的是C語言,不同語言的讀者只好換個位啦,畢竟讀C的人較多~
所謂的快速冪,實際上是快速冪取模的縮寫,簡單的說,就是快速的求一個冪式的模(餘)。在程式設計過程中,經常要去求一些大數對於某個數的餘數,為了得到更快、計算範圍更大的演算法,產生了快速冪取模演算法。

我們先從簡單的例子入手:求ab % c = ?

【演算法1.首先直接地來設計這個演算法:】

//直接算出a^b的結果
int ans = 1;
for(int i = 1;i<=b;i++)
{
ans = ans * a;
}
ans = ans % c;

那麼,我們先來看看第一個改進方案:在講這個方案之前,要先有這樣一個公式(開頭概述裡已經紅色字型標出了)
ab%c=(a%c)b%c
這個公式大家在離散數學或者數論當中應該學過,不過這裡為了方便大家的閱讀,還是給出引理:
引理1:ab%c = (a%c)b%c
上面公式為下面公式的引理,即積的取餘等於取餘的積的取餘。證明了以上的公式以後,我們可以先讓a關於c取餘,這樣可以大大減少a的大小,
於是不用思考的進行了改進:

【演算法2:】

int ans = 1;
a = a % c; //加上這一句,先對a取餘
for(int i = 1;i<=b;i++)
{
    ans = ans * a;
}
ans = ans % c;

聰明的讀者應該可以想到,既然某個因子取餘之後相乘再取餘保持餘數不變,那麼新算得的ans也可以進行取餘,所以得到比較良好的改進版本。
【演算法3】

int ans = 1;
a = a % c; //加上這一句,先對a取餘
for(int i = 1;i<=b;i++)
{
  ans = (ans * a) % c;//這裡再取了一次餘【改進】
}
ans = ans % c;

這個演算法在時間複雜度上沒有改進,仍為O(b),不過已經好很多的,但是在c過大的條件下,還是很有可能超時,所以,我們推出以下的快速冪演算法。
快速冪演算法依賴於以下明顯的公式,我就不證明了
這裡寫圖片描述

那麼我們可以得到以下演算法:

【演算法4:】

int ans = 1;
a = a % c;
if(b%2==1)
ans = (ans * a) mod c; //如果是奇數,要多求一步,可以提前算到ans中

k = (a*a) % c; //我們取a^2而不是a
for(int i = 1;i<=b/2;i++)
{
ans = (ans * k) % c;
}
ans = ans % c;

我們可以看到,我們把時間複雜度變成了O(b/2).當然,這樣子治標不治本。但我們可以看到,當我們令k = (a * a) mod c時,狀態已經發生了變化,我們所要求的最終結果即為(k)b/2 mod c而不是原來的ab mod c,所以我們發現這個過程是可以迭代下去的。當然,對於奇數的情形會多出一項a mod c,所以為了完成迭代,當b是奇數時,我們通過
ans = (ans * a) % c;來彌補多出來的這一項,此時剩餘的部分就可以進行迭代了。
形如上式的迭代下去後,當b=0時,所有的因子都已經相乘,演算法結束。於是便可以在O(log b)的時間內完成了。於是,有了最終的演算法:快速冪演算法。

【演算法5:快速冪演算法】

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) % 
    b = b/2;
    a = (a * a) % c;

   }
return ans;
}

本演算法的時間複雜度為O(logb),能在幾乎所有的程式設計(競賽)過程中通過,是目前最常用的演算法之一。

附上比較流行的快速冪取餘程式碼

int mod_exp(int a, int b, int c)        //快速冪取餘a^b%c
{
    int res, t;
    res = 1 % c; 
    t = a % c;
    while (b)
    {
        if (b & 1)//b的二進位制和1的二進位制進行位的按位與運算,其實等價於if(b%2==1)起到判斷奇偶的功能,但計算機的位運算比較快。
        {
            res = res * t % c;
        }
        t = t * t % c;
        b >>= 1;//即b=b>>1 b的二進位制數右移一位賦值給b,右移時高位空缺補零。類比十進位制數100右移相當於100/10=10;b>>1 等價於b/2;但計算機的位運算比較快。
    }
    return res;
}

相關推薦

快速演算法思路

【概述】 計算xy % n;如果採用常規方法,當x與y都比較小的情況下,採用直接計算可以,但是如果當x跟y都非常大的時候,如21000 % 100000,那該如何解決呢? 【離散數學有關餘數知識點補充】 重視餘數的性質: 1. (a+b

轉載ACM 快速演算法

快速冪取模的用途:在ACM這類競賽中,可能會遇到指數型的資料取模問題,這個時候如果直接用int或者long long儲存,就 有可能會超出計算機整數的存取範圍,而導致資料出錯。所以我們需要一種方法進行計算。而這種方法就是我們這次要講到 的快速冪取模(簡稱快速冪)。這種演算法在時間和空間上都做了儘可能的優化

快速(大數運算/演算法優化)

快速冪取餘 int PowerMod(int a, int b, int k) { int ans = 1; a = a % k; while(b>0))

洛谷P1226 快速||運算 題解

題目描述 輸入b,p,k的值,求b^p mod k的值。其中b,p,k*k為長整型數。 輸入輸出格式 輸入格式: 三個整數b,p,k. 輸出格式: 輸出“b^p mod k=s” s為運算結果 輸入輸出樣例 輸入樣例#1: 2 10 9 輸出樣例#

【模板】快速||運算。

拿一個樣例說話吧: 2^1=2 2%9=2 2^2=4 4%9=4 2^3=8 8%9=8 2^4=16 16%9=7 2^5=32 32%9=5 2^6=64 64%9=1 2^7=128 128%9=2 通過這個你能發現什麼呢? 自然就是餘數都是有規律的。 是不是讓快速冪變得淺顯易懂了。

快速(模)演算法

對於普通型別的求a^n,我們的求法是不是a*a*a*a....,這樣乘以n次,時間複雜度為O(n),對於普通n比較小的我們可以接受,然而當n比較大的時候,計算就慢了,所以我們就去尋找更快捷的計算方法! 例如:我們要求2^8,我們通過當為偶數的時候,a^n=(a*a)^(n/

HDU1061 Rightmost Digit(快速)

Rightmost Digit Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 68789    Acc

快速||運算

https://www.luogu.org/problemnew/show/P1226 快速冪和取餘性質學習了題解。 1 #include<iostream> 2 #include<algorithm> 3 #include<cstdio>

HDU 1097 快速(C語言)

A hard puzzle Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 39772    Accepted

快速演算法小結

快速冪取模演算法,是比較常見的演算法。分享給大家供大家參考之用。具體如下: 首先,所謂的快速冪,實際上是快速冪取模的縮寫,簡單的說,就是快速的求一個冪式的模(餘)。在程式設計過程中,經常要去求一些大數對於某個數的餘數,為了得到更快、計算範圍更大的演算法,產生了快速冪取模

NEFU1493 快速+除法(逆元)

題目: Gugu 有兩個長度無限長的序列A,B A0=a^0/0!,A1=a^1/1!,A2=a^2/2!,A3=a^3/3!…. B0=0, B1=b^1/1!,B2=0,B3=b^3/3!,B4=0, B5=b^5/5! … Douge

【洛谷】P1226 【模板】快速||運算

題目連結 題目描述 輸入b,p,k的值,求b^p mod k的值。其中b,p,k*k為長整型數。 輸入輸出格式 輸入格式: 三個整數b,p,k. 輸出格式: 輸出“b^p mod k=s” s為運算結果 輸入輸出樣例 輸入樣例#1: 複製

(復學梳理) 快速求模[程式碼思想]

首先,給出程式碼: const LL mod = 1000000007; LL quick(LL a,LL b) { LL ans=1; a=a%mod; while(b!=0) { if(b&

快速演算法

演算法2:另一種演算法利用了二分的思想,可以達到O(logn)。可以把b按二進位制展開為:b = p(n)*2^n  +  p(n-1)*2^(n-1)  +…+   p(1)*2  +  p(0) 其中p(i) (0<=i<=n)為 0 或 1 這樣 a^b =  a^ (p(n)*2^n 

簡單易懂的快速演算法

本文是上一篇文章《程式設計師必學:快速冪演算法》的續集,上一篇文章詳細地介紹了快速冪演算法,提供了遞迴、非遞迴的2種實現方案 丟擲問題 請設計一個演算法求x的y次冪模z的結果:(x ^ y) % z x、y、z都是整數 z ≠ 0, y ≥ 0 x、y的絕對值可能很大,比如(1234 ^ 4567) %

快速(C語言版)

在百度文庫上下載的快速冪詳解,作者給出快速冪演算法的完整解釋(雖然我也還沒看懂,但是確實寫的很好,正在仔細研究中)用的是C語言,不同語言的讀者只好換個位啦,畢竟讀C的人較多~(原網址http://wenku.baidu.com/link?url=AQNEjQ6S-31iyR

【Java面試12】常用演算法(冒泡、插入、選擇、快速)和二叉樹

常用演算法(冒泡、插入、選擇、快速)和二叉樹詳解     同一問題可用不同演算法解決,而一個演算法的質量優劣將影響到演算法乃至程式的效率。演算法分析的目的在於選擇合適演算法和改進演算法。   電腦科學中,演算法的時間複雜度是一個函式,它定量描述了該演算法的執行時間。這是一個關於

演算法競賽寶典 分治演算法 快速模運算

//理解快速冪運算即可,類似於加法//理解快速冪運算即可,類似於加法 #include<iostream> #include<algorithm> #include<bi

快速快速演算法超級詳細介紹

        今天在網上看了一些快速冪取模演算法的介紹,總體感覺要麼文章介紹的很簡略,導致我搞了半天才搞明白什麼意思,還有的文章直接放上了錯誤的程式碼,真是坑爹啊!所以我就特意寫一篇文章方便大家理解一下這個演算法的原理和程式碼是什麼意思。原理介紹:     目標:快速求出 

快速模和快乘

要去 ont pow 取模 當下 tex str 過程 return 一、快速冪取模概念   快速冪取模,顧名思義,就是快速的求一個冪式的模(余),比如a^b%c,快速的計算出這個式子的值。   在程序設計過程中,經常要去求一些大數對於某個數的余數,為了得到更快、計算範圍更