擴展歐幾里得求數字逆元


  歐幾里得算法大家應該都聽說過,是一個求最大公約數的算法,又叫輾轉相除法。大致算法的思路就是,要求a,b兩個數的最大公約數,用其中一個數對另一個數取餘數,不妨記爲b%a,然後讓下一輪,b變爲a,a再變爲上一輪b%a的餘數繼續重複這樣的操作。
  這裏簡單給出一個證明。設最大公約數爲t,則a=st,b=lta=st, b=lt,則b%a=kt, klk<sb\%a=kt,\ k≤l且k<s,所以這是一個縮小的序列,最終k縮小到0,就得到了最大公約數。
  那麼什麼是擴展歐幾里得算法呢,這裏得先介紹一個定理,叫裴蜀定理,對於a,b肯定存在as+bt=gcd(a,b)as+bt=gcd(a,b),能夠在求公約數的時候把x,y也求出來呢,這就是擴展歐幾里得算法的用處。算法在求公約數的時候加上了兩組數列,這數列在每一步的時候,計算一個等式,使得每一步都有b%a=as+btb\%a=as+bt,具體細節就不展開了。顯然這個公式中,最後一步,求出a,b的最大公約數時,也就求出了裴蜀定理中的x和y。
  求出這個有什麼用呢,這個公式就是用來計算乘法逆元的。先解釋一下逆元的概念,假如有一個質數p,有一個正數a,存在a對p的逆元a1滿a1a%p=1a^{-1},滿足a^{-1}*a\%p=1,則a1a^{-1}就是a對p的逆元。可以證明對於a小於質數p,逆元總是存在且唯一的。
  把逆元的公式寫成裴蜀定理的形式,aa1+tp=1=gcd(a,p)a*a{-1}+tp=1=gcd(a,p),因爲p是質數,所以對於a<pa<p兩個數的公約數就是1,然後對應上去歐幾里得算法,我們要求的就是裴蜀定理中的s。但是直接求出來s可能是負數,我們需要讓s對p求一次補,把它轉成正數。
  但是我們到這裏只關心這個s,這個s也是一個序列,滿足上面的等式,可以看到s的迭代公式也是和b%a的餘數迭代公式類似的,具體證明可以大家自己查閱資料。下面直接給出代碼。緩衝一下,先看看gcd的代碼,然後大家自己做個對比。顯然兩個算法都是求出公約數的時候停止。

python求公約數的代碼

def gcd(a, b):
    while a:
        a,b= b%a ,a
    return b

python擴展歐幾里得求逆元代碼

def ext_euclid(a, b): # b表示需要輸取模的質數
    old_s,s=1,0
    old_r,r=a,b
    if b == 0:
        return 
    else:
        while(r!=0):
            q=old_r//r
            old_r,r=r,old_r-q*r
            old_s,s=s,old_s-q*s
    return old_s%b

C++擴展歐幾里得求逆元代碼

int ext_euclid(long a, long b)
{
	if (b == 0)return 0;
	long old_r = a, r = b;
	long old_s = 1, s = 0;
	while (old_r % r)
	{
		long q = old_r / r, tmp = old_r;
		old_r = r;
		r = tmp % r;
		tmp = old_s;
		old_s = s;
		s = tmp - s * q;
		cout << r << ',' << s << endl;
	}
	return s>0? s:b+s;
}

  s和t的遞推式其實很好證明,只需要根據r的遞推式做一個等式代換即可,然後就可以把s和t都求出來,但是我們這裏只需要求出s即可。

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