快速冪取模快速演算法超級詳細介紹
今天在網上看了一些快速冪取模演算法的介紹,總體感覺要麼文章介紹的很簡略,導致我搞了半天才搞明白什麼意思,還有的文章直接放上了錯誤的程式碼,真是坑爹啊!所以我就特意寫一篇文章方便大家理解一下這個演算法的原理和程式碼是什麼意思。
原理介紹:
目標:快速求出 ab Mod c (注意:b是一個大數)
數學原理工具: (a*b) Mod c = [(a Mod c)*(b Mod c)] Mod c (加上括號是為了方便理解運算順序,證明不難,可以自己百度)
考慮到b是一個大數,直接算 ab 會很慢,所以首先把 b 轉換成二進位制形式(這個轉換不用再寫一個程式,計算機裡面就是二進位制保持的)
b=(bnbn-1bn-2…b3b2b1b0)2
b=b0 + b1*21 + b2*22 + b3*23 + b4*24 +…+ bn-1*2n-1 + bn*2n
ab = ab0+b1*2^1 + b2*2^2+ b3*2^3 + b4*2^4 +…+ bn-1*2^(n-1) + bn*2^n
= ab0 * ab1*2^1 * ab2*2^2 * ab3*2^3 *…* abn*2^n
ab % c = (ab0*ab1*2^1*ab2*2^2*ab3*2^3*…*abn*2^n) % c
設 An=(ab0*ab1*2^1*ab2*2^2
考慮到 (a*b) Mod c = [(a Mod c)*(b Mod c)] Mod c
An=[(ab0*ab1*2^1*ab2*2^2*…*ab(n-1)*2^(n-1))%c*(abn*2^n)%c]%c
=[ An-1* (abn*2^n) % c ] % c
為了方便這裡設 Kn=(abn*2^n) % c 於是我們就得到一個遞推關係
An = (An-1*Kn) % c
A0 = ab0 % c
這裡有一個問題就是Kn怎麼求,考慮到Kn和bn有關,當bn取0時,Kn=1.當bn取1時,Kn = (a2^n
(a2^n) % c = ( a2^(n-1)* a2^(n-1))% c
= [ (a2^n-1) % c * (a2^n-1) % c ] % c
所以可以設 Tn= (a2^n) % c 那麼可以得到
Tn= (a2^n) % c
= ( a2^(n-1)* a2^(n-1))% c
= [(a2^n-1) % c * (a2^n-1) % c ] % c
= ( Tn-1 * Tn-1 ) % c
得到以下遞推關係
Tn=( Tn-1 * Tn-1 ) % c
T0= a % c
現在總結以下,首先我們的目標是求出An,我們已經得到遞推關係,但是在遞推過程中我們還要Kn,所以我們得計算Kn,而Kn和bn有關,當bn=0,Kn=1,當bn=1,Kn=Tn,考慮到我們可能在任何地方需要Tn,因為b的二進位制的1的位置是都有可能的,所以我們需要一直計算Tn,就是說當bn=0時我們也是要計算Tn的,因為可能以後會用到,而求Tn我們也已經給出了遞推式,所以問題可以解決了。下面看一下程式碼
程式碼:
int quick(int a,int b,int c)
{
int A=1; //結果的儲存,就是An,初始化一下
T=a%c; //首先計算T0的值,用於Tn的遞推
while(b!=0)
{
//這個if是判斷目前最右邊的一位bn是不是1,如果是1,那麼Kn=Tn直接用Tn遞推,具體看上面原理,如果bn=0,那麼Kn=1,考慮到An-1是小於c的,所以 An=(An-1)%c =An-1 就是說可以不用計算了 因為相當於直接 A=A
if(b&1) {
A = ( A * T ) % c;
}
b>>=1; //二進位制位移,相當於從右到左讀取位b0 b1 b2 b3 b4等等
T=(T*T)%c; //更新T,如果下一位是1就可以用這個算A,具體的可以看上面原理的遞推關係
}
return A;
}