歐幾里得&擴展歐幾里得算法及相關的數學證明

//歐幾里得算法求最大公約數
int gcd(int m,int n)
{
         int u0=m,u1=n,t;
         if( u0<u1 )// 保證u0>=u1
                u0^=u1,u1^=u0,u0^=u1;
         while(u0%u1)
        {
                t=u1;
                u1=u0%u1;
                u0=t;
        }
         return u1;
}
//遞歸版本
int gcd(int m, int n)
{
         return n==0?m:gcd(n,m%n);
}
//擴展歐幾里得算法求ax+by==gcd(a,b)的解,其中g是最大公約數,此算法根據結論gcd(a,b)是a和b的最小的正線性組合得出(特殊情況,當gcd(a,b)爲1時候,解x爲a的逆元)
void gcdex(int m,int n, int& g,int & x,int& y)
{
         if(n==0)        
        {
                 //gcd(m,0)=1*m-0*0=m
                g=m; x=1; y=0;
        }
         else            
        {
                gcdex(n,m%n,g,y,x);     
                y-=x*(m/n);
        }
}
//求最小公倍數
//求n個數最小公倍數,(除去所有的公約數,就成了最小公倍)
int lcm(int val[],int n)
{
     int res=1;
     for (int i = 0;i < n;i ++)
                 for (int j = i + 1;j <= n;j++)
                         if (val[j] % val[i] == 0)
                                val[j] /= val[i];
     for(int j=0; j<n; j++)
               res*=val[j];
       return res;
}



//以下介紹來自網絡

RSA算法中利用歐幾里得算法求d詳細過程

RSA是第一個也是使用的最廣泛的公鑰加密算法,在1978年由R.Rivest、AdiShamir和Adleman三人發明,並以他們的名字命名。RSA算法的安全性基於大數因子分解的困難性,下面介紹一下它的基本原理:


1、生成公鑰和私鑰

(1)選取兩個大素數:p和q;
(2)計算n=p*q;
(3)計算小於n並且與n互質的整數的個數,即歐拉函數Ø(n)=(p-1)*(q-1);
(4)隨機選擇加密密鑰e,使1<e<Ø(n),且與Ø(n)互質;
(5)最後,利用Euclid(歐幾里得)算法計算解密密鑰d,使其滿足ed=1(mod Ø(n))。

然後將(e,n)公開,即爲公鑰PK,私人保存好d,即爲私鑰SK;

2、加密

將明文m分解成等長數據塊m1,m2,……,mi。加密時,按如下公式進行計算即可:

ci=(mie(mod n),密文c則由c1,c2,……ci組成。

 

3、解密

與加密一樣,按如下公式進行計算:

mi=(cid(mod n),明文m則由m1,m2,……,mi組成。


以上就是RSA算法的公私鑰產生、加密和解密的過程。整個過程中,最難理解的部分應是1.5中的求私鑰d,很多課本提到的都是用歐幾里得算法,但並未給出具體的計算過程,下面本人就通過一個實例向大家介紹歐幾里得算法在RSA中的應用。

例:令p=47,q=71,求用RSA算法加密的公鑰和私鑰。

計算如下:

(1)n=pq=47*71=3337;
(2)Ø(n)=(p-1)*(q-1)=46*70=3220;
(3)隨機選取e=79(滿足與3220互質的條件);
(4)則私鑰d應該滿足:79*d mod 3220 = 1;

那麼這個式子(4)如何解呢?這裏就要用到歐幾里得算法(又稱輾轉相除法),解法如下:

(a)式子(4)可以表示成79*d-3220*k=1(其中k爲正整數);
(b)將3220對79取模得到的餘數60代替3220,則變爲79*d-60*k=1;
(c)同理,將79對60取模得到的餘數19代替79,則變爲19*d-60*k=1;
(d)同理,將60對19取模得到的餘數3代替60,則變爲19*d-3*k=1;
(e)同理,將19對3取模得到的餘數1代替19,則變爲d-3*k=1;

當d的係數最後化爲1時,

令k=0,代入(e)式中,得d=1;
將d=1代入(d)式,得k=6;
將k=6代入(c)式,得d=19;
將d=19代入(b)式,得k=25;
將k=25代入(a)式,得d=1019,這個值即我們要求的私鑰d的最終值。

此時,我們即可得到公鑰PK=(e,n)={79,3337},私鑰SK={1019,3337},後面的加密和解密直接套相應公式即可。

 
#include <iostream>

//擴展歐幾里得算法的特殊情況,求乘法逆元
unsigned inverse(unsigned prime, unsigned mod)
{
     unsigned res;//記錄計算結果
     if (prime == 1)
          return prime;
     else if (prime == 0)
          return 0;
     else
     {
          res = ( mod>prime ? inverse( prime, mod%prime):inverse(prime%mod, mod) );
          return ( mod>prime ? ( (mod*res+1)/prime ) : ( (prime*res-1)/mod) );
     }
}

int main()
{
     printf("%d",inverse(79,3220) );
     getchar();
     return 0;
}



//附上網絡搜來的擴展歐幾里得算法的證明

什麼是GCD
GCD是最大公約數的簡稱(當然理解爲我們偉大的黨也未嘗不可)。在開頭,我們先下幾個定義:
①a|b表示a能整除b(a是b的約數)
②a mod b表示a-[a/b]b([a/b]在Pascal中相當於a div b)
③gcd(a,b)表示a和b的最大公約數
④a和b的線性組合表示ax+by(x,y爲整數)。我們有:若d|a且d|b,則d|ax+by(這很重要!)

線性組合與GCD
現在我們證明一個重要的定理:gcd(a,b)是a和b的最小的正線性組合。
證明:
設gcd(a,b)爲d,a和b的最小的正線性組合爲s
∵d|a且d|b,
∴d|s。
而a mod s=a-[a/s]s
         =a-[a/s](ax+by)
         =a(1-[a/s]x)-b[a/s]y
亦爲a和b的線性組合
∵a mod s<s,a mod s不能是a和b的最小的正線性組合
∴a mod s=0,即s|a
同理由s|b
∴s爲a,b的公約數
∴s<=d
∵d|s
∴d=s。證畢。

由這條定理易推知:若d|a且d|b,則d|gcd(a,b)

Euclid算法
現在的問題是如何快速的求gcd(a,b)。窮舉明顯不是一個好方法(O(n)),所以需要一個更好的方法。
首先我們先提出一個定理:gcd(a,b)=gcd(b,a-bx)(x爲正整數)。

證明:
設gcd(a,b)=d,gcd(b,a-bx)=e,則
∵d|a,d|b
∴d|a-bx
∴d|gcd(b,a-bx),即d|e
∵e|b,e|a-bx
∴e|bx+(a-bx),即e|a
∴e|gcd(a,b),即e|d
∴d=e。證畢。

這個定理非常有用,因爲它能快速地降低數據規模。
當x=1時,gcd(a,b)=gcd(b,a-b)。這就是輾轉相減法。
當x達到最大時,即x=[a/b]時,gcd(a,b)=gcd(b,a mod b)。這個就是Euclid算法。它是不是Euclid提出的我不知道,但聽說是在Euclid時代形成的,所以就叫Euclid算法了。程序非常的簡單:

functionEuclid(a,b:longint):longint;
 begin
  if b=0 then exit(a)
         else exit(Euclid(b,a mod b));
 end;

Euclid算法比輾轉相減法好,不僅好在速度快,而且用起來也方便。兩種算法都有一個隱含的限制:a>=b。用輾轉相減法時,必須先判斷大小,而Euclid算法不然。若a<b,則一次遞歸就會轉爲gcd(b,a),接着就能正常運行了。

擴展Euclid
前面我們說過,gcd(a,b)可以表示爲a和b的最小的正線性組合。現在我們就要求這個最小的正線性組合ax+by中的x和y。這個可以利用我們的Euclid算法。
從最簡單的情況開始。當b=0時,我們取x=1,y=0。當b≠0時呢?
假設gcd(a,b)=d,則gcd(b,a mod b)=d。若我們已經求出了gcd(b,a mod b)的線性組合表示bx'+(a mod b)y',則
gcd(a,b)=d
        =bx'+(a mod b)y'
        =bx'+(a-[a/b]b)y'
        =ay'+b(x'-[a/b]y')
那麼,x=y',y=x'-[a/b]y'。這樣就可以在Euclid的遞歸過程中求出x和y。

程序:
function gcd(a,b:longint):longint;
 var p,n,m:longint;
 begin
  if b=0 then
   begin
    x:=1;
    y:=0;
    exit(a);
   end
  else
   begin
    p:=gcd(b,a mod b);
    n:=x;
    m:=y;
    x:=m;
    y:=n-a div b*m;
    exit(p);
   end;
 end;

我們現在還有一個問題:x,y是不是確定的?答案:不是。如果x,y符合要求,那麼x+bk,y-ak也符合要求。不確定的原因在於這一句:“當b=0時,我們取x=1,y=0。”實際上y可以取任何正整數。

不定方程ax+by=c
現在終於到了本文重點:解二元一次不定方程。看起來擴展Euclid算法是不定方程的一種特殊情況,實際上呢,不定方程卻是用Euclid算法解的。
對 於不定方程ax+by=c,設gcd(a,b)=d,如果ax+by=c有解,則d|c(這也是許多奧數題的切入點)。所以一旦d不是c的約數,那麼 ax+by=c一定無解。當d|c時,先求出ax'+by'=d=gcd(a,b)的x'和y',則x=x'*c/d,y=y'*c/d。由上一段可知, 只要ax+by=c有一個解,它就有無數個解。
Euclid算法還可以求解同餘方程ax≡b(mod m)。這其實和不定方程ax+my=b沒有區別。(不定方程和同餘方程一般都有範圍限制,這其實也很容易解決,就不說了)


發佈了59 篇原創文章 · 獲贊 1 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章