算法與數據結構-數論之蒙哥馬利模乘

        對於乘模運算 A*B%N,如果A、B都是1024位的大數,先計算A*B,再% N,就會產生2048位的中間結果,如果不採用動態內存分配技術就必須將大數定義中的數組空間增加一倍,這樣會造成大量的浪費,因爲在絕大多數情況下不會用到那額外的一倍空間,而採用動態內存分配技術會使大數存儲失去連續性而使運算過程中的循環操作變得非常繁瑣。所以模乘運算的首要原則就是要避免直接計算A*B。

設A=Sum[i=0 to k](A*r**i),r=0x10000000,0<=A<r,則:C = A*B = Sum[i=0 to n](A*B*r**i) %N
可以用一個循環來處理:

C=0;
FOR i=n to 0
C=C*r
C=C+A*B

RETURN C

這樣將一個多位乘法轉換成了一系列單位乘法和加法,由於:

a*b %n = (a%n * b%n) %n
a+b %n = (a%n + b%n) %n

所以,對於求C=A*B %N,我們完全可以在求C的過程中完成:

C=0;
FOR i=n to 0
C=C*r %N
C=C+A*B
%N
RETURN C

這樣產生的最大中間結果是A*B
或C*r,都不超過1056位,空間代價會小得多,但是時間代價卻加大了,因爲求模的過程由一次變成了多次。對於孤立的乘模運算而言這種時間換空間的交易還是值得的,但是對於反覆循環的乘模運算,這種代價就無法承受,必須另尋出路。

不難發現,模乘過程中複雜度最高的環節是求模運算,因爲一次除法實際上包含了多次加法、減法和乘法,如果在算法中能夠儘量減少除法甚至避免除法,則算法的效率會大大提高。
設A=Sum[i=0 to k](A*2**i),0<=A<=1,則:
C= A*B = Sum[i=0 to k](A
*B*2**i)

可用循環處理爲:
C=0
FOR i FROM k TO 0
C=C*2
C=C+A
*B
RETURN C

若令 C'= A*B*2**(-k),則:
C'= Sum[i=0 to k](A
*B*2**(i-k))

用循環處理即:
C'=0
FOR i FROM 0 TO k
C'=C'+A
*B
C'=C'/2
RETURN C'

通過這一算法求A*B*2**(-k)是不精確的,因爲在循環中每次除以2都可能有餘數被捨棄了,但是可以通過這一算法求A*B*2**(-k) %N的精確值,方法是在對C'除2之前,讓C'加上C'[0]*N。由於在RSA中N是兩個素數的積,總是奇數,所以當C'是奇數時,C'[0]=1,C'+C'[0]*N 就是偶數,而當C'爲偶數時C'[0]=0,C'+C'[0]*N還是偶數,這樣C'/2 就不會有餘數被捨棄。又因爲C'+N %N = C' %N,所以在計算過程中加若干次N,並不會影響結果的正確性。

可以將算法整理如下:
C'=0
FOR i FROM 0 TO k
C'=C'+A
*B
C'=C'+C'[0]*N
C'=C'/2
IF C'>=N C'=C'-N
RETURN C'

由於在RSA中A、B總是小於N,又0<=A
,C'[0]<=1,所以:
C' = (C'+A
*B+C'[0]*N)/2
C' < (C'+2N)/2
2C' < C'+2N
C' < 2N

既然C'總是小於2N,所以求C' %N 就可以很簡單地在結束循環後用一次減法來完成,即在求A*B*2**(-k) %N的過程中不用反覆求模,達到了我們避免做除法的目
的。當然,這一算法求得的是A*B*2**(-k) %N,而不是我們最初需要的A*B %N。但是利用A*B*2**(-k)我們同樣可以求得A**E %N。

設R=2**k %N,R'=2**(-k) %N,E=Sum[i=0 to n](E
*2**i):
A'=A*R %N
X=A'
FOR i FROM n TO 0
X=X*X*R' %N
IF E
=1 X=X*A'*R' %N
X=X*1*R' %N
RETURN X

最初:
X = A*R %N,

開始循環時:
X = X*X*R' %N
= A*R*A*R*R' %N
= A**2*R %N

反覆循環之後:
X = A**E*R %N

最後:
X = X*1*R' %N
= A**E*R*R' %N
= A**E %N

如此,我們最終實現了不含除法的模冪算法,這就是著名的蒙哥馬利算法,而X*Y*R' %N 則被稱爲“蒙哥馬利模乘”。以上討論的是蒙哥馬利模乘最簡單,最容易理解的二進制形式。蒙哥馬利算法的核心思想在於將求A*B %N轉化爲不需要反覆取模的A*B*R' %N,但是利用二進制算法求1024位的A*B*R' %N,需要循環1024次之多,我麼必然希望找到更有效的計算A*B*R' %N的算法。

考慮將A表示爲任意的r進制:
A = Sum[i=0 to k](A
*r**i) 0<=A<=r

我們需要得到的蒙哥馬利乘積爲:
C'= A*B*R' %N R'=r**(-k)

則以下算法只能得到C'的近似值
C'=0
FOR i FROM 0 TO k
C'=C'+A
*B
C'=C'/r
IF C'>=N C'=C'-N
RETURN C'

因爲在循環中每次C'=C'/r 時,都可能有餘數被捨棄。假如我們能夠找到一個係數 q,使得(C' + A
*B + q*N) %r =0,並將算法修改爲:
C'=0
FOR i FROM 0 TO k
C'=C'+A
*B+q*N
C'=C'/r
IF C'>=N C'=C'-N
RETURN C'

則C'的最終返回值就是A*B*R' %N的精確值,所以關鍵在於求q。由於:
(C' + A
*B + q*N) %r =0
==> (C' %r + A
*B %r + q*N %r) %r =0
==> (C'[0] + A
*B[0] + q*N[0]) %r =0

若令N[0]*N[0]' %r =1,q=(C'[0]+A
*B[0])*(r-N[0]') %r,則:
(C'[0] + A
*B[0] + q*N[0]) %r
= (C'[0]+A
*B[0] - (C'[0]+A*B[0])*N[0]'*N[0]) %r) %r
= 0

於是我們可以得出r爲任何值的蒙哥馬利算法
m=r-N[0]'
C'=0
FOR i FROM 0 TO k
q=(C'[0]+A
*B[0])*m %r
C'=(C'+A
*B+q*N)/r
IF C'>=N C'=C'-N
RETURN C'

如果令 r=0x100000000,則 %r 和 /r 運算都會變得非常容易,在1024位的運算中,循環次數k 不大於32,整個運算過程中最大的中間變量C'=(C'+A
*B+q*N)< 2*r*N < 1057位,算法效率就相當高了。唯一的額外負擔是需要計算 N[0]',使N[0]*N[0]' %r =1,而這一問題前面已經用歐幾里德算法解決過了,而且在模冪運算轉化成反覆模乘運算時,N是固定值,所以N[0]'只需要計算一次,負擔並不大。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章