數論之擴展歐幾里得,費馬小定理,歐拉定理 + 求最小乘法逆元

前兩天二刷了《模仿遊戲》,Alan Turing在二戰中研製的圖靈機破譯了德軍號稱牢不可破的Enigma密碼機。這部劇讓我對計算機產生了一些新的理解,結合以前修過的密碼學原理課程,因此想記錄一下之前沒掌握好的數論知識,並且以RSA公鑰密碼爲例,解一道經典題。

1 逆元定義

ax1(modb)a\,*\,x\,≡\,1\,(mod\,b),且 a 與 b 互斥,那麼就能定義 x 爲 a 的逆元,記爲a1a^{-1},也可稱爲 x 爲 a 的倒數。

2 歐幾里得算法(求最大公約數)

首先介紹古老而又強大的歐幾里得算法(又稱輾轉相除法):

兩個數 a 和 b 的最大公因子(greatest common divisior)是能整除它們兩者的最大整數。歐幾里德算法用於計算兩個整數 a,b 的最大因子。記 gcd(a,b)爲自然數 a與 b的最大公因子。特別的,有 gcd(0, n) = 0,因爲任何整數都能整除 0。

內容:
gcd(a,b)={a,b=0gcd(b,amodb),b0 gcd(a\,,b)=\left\{ \begin{aligned} a&, \,b\,=\,0 \\ gcd(b\,,\,a\,mod\,b)&, \,b \neq 0 \end{aligned} \right.
代碼

int gcd(int a, int b){   
    return b == 0 ? a : gcd(b, a % b);     
}

3 擴展歐幾里得算法

3.1 預備知識

1.取模運算

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

取模運算對除法不成立,當要求(a / b) % c時,可轉化爲逆元來求:
(a/b)%c=(ab1)%c=(a%c    b1%c)%c (a\,/\,b)\,\%\,c\,=\,(a\,*\,b^{-1})\,\%\,c\,=\,(a\,\%\,c\,\,*\,\,b^{-1}\,\%\,c)\,\%\,c
這就是逆元的作用。
2.裴蜀定理:

給予兩個整數 a,b,必存在整數 x,y 使得ax+by=gcd(a,b)ax+by=gcd(a,\,b)

即如若 ax+by=cax+by=c 有解,那麼有 gcd(a,b)cgcd(a,\,b)\,|\,c(c一定是gcd(a,b)gcd(a,\,b)的若干倍)

特例:當c=1c = 1時,如果 ax+by=1ax+by=1 有解,那麼 gcd(a,b)=1gcd(a,\,b)\,=\,1

3.2 關於擴展歐幾里得算法

  • 擴展歐幾里得算法(Extended Euclidean algorithm)是歐幾里得算法的擴展。它可用來求解形如ax+by=c(a,b,cZax+by=c\,(a,b,c\,\in Z)方程的一組整數解。
  • 對於求解方程ax+by=c(gcd(a,b)c)ax+by=c\,(gcd(a,\,b)\,|\,c),只需求解出方程ax+by=gcd(a,b)ax+by=gcd(a,\,b)的一組解,將 x,yx,y 分別乘上cgcd(a,b)\frac {c}{gcd(a,b)}即可得到方程 ax+by=c(gcd(a,b)c)ax+by=c\,(gcd(a,\,b)\,|\,c) 的一組解。
  • 擴展歐幾里得算法可以用來計算模反元素(也叫模逆元),而模反元素在RSA加密算法中有舉足輕重的地位。

3.3 模板

(思考了一下該先放模板,還是先放算法的證明過程,最後決定先放模板, 然後就着模板理解算法的證明過程)

ll ex_gcd(ll a, ll b, ll &x, ll &y){
	//標記1
	if(b == 0){
		x = 1, y = 0;
		return a;
	}
	ll d = ex_gcd(b, a % b, x, y);
	ll temp = y;
	//標記2
	y = x - (a / b) * y;
	x = temp;
	//標記3
	return d;
}

3.4 算法推導過程

現有方程:ax+by=gcd(a,b)ax+by=gcd(a,\,b)
ex_gcd(a, b, x, y)爲求解上述方程的函數,函數返回的是gcd(a,b)gcd(a,b)最大公約數(對應模板代碼標記3),其中形參 x,yx,y引用參數(全局變量),也是上述方程的解

  • 邊界情況(對應模板代碼標記1):
    b=0b=0 時,方程爲ax=gcd(a,0)ax=gcd(a,0),解得
    {x=1y=0 \left\{ \begin{array}{l} x=1 \\ y=0 \end{array} \right.此時a就是gcd(a,0)gcd(a,0)的最大公約數,因此函數return a
  • 一般情況(對應模板代碼標記2):
    b0b\neq0時,有歐幾里得算法
    gcd(a,b)=gcd(b,a%b) gcd(a\,,b)\,=\,gcd(b^\prime\,,\,a^\prime\,\%\,b^\prime) 則有方程
    ax+by=gcd(a,b)=gcd(b,a%b) ax+by=gcd(a,\,b)=gcd(b^\prime\,,\,a^\prime\,\%\,b^\prime) 另外有等式(不是啥公式,一個運算技巧):
    a%b=a(a/b)b a\,\%\,b=a-(a/b)*b 則方程可化爲
    gcd(b,a%b)=bx+(a%b)y=bx+(a(a/b)b)y gcd(b^\prime\,,\,a^\prime\,\%\,b^\prime)=b^\prime x^\prime+(a^\prime\,\%\,b^\prime)y^\prime=b^\prime x^\prime+(a^\prime-(a^\prime/b^\prime)*b^\prime)y^\prime 上式化簡得:
    bx+ay(a/b)by=ay+b(xa/by) b^\prime x^\prime+a^\prime y^\prime-(a^\prime / b^\prime)*b^\prime*y^\prime=a^\prime y^\prime+b^\prime*(x^\prime-a^\prime /\,b^\prime*y^\prime)
    於是可以得到關於方程解x,yx,y的遞推關係:
    {x=yy=xa/by \left\{ \begin{array}{l} x=y^\prime \\ y=x^\prime-a^\prime /\,b^\prime*y^\prime \end{array} \right.
  • 由此我們得到了邊界條件以及遞歸式,即每次遞歸ex_gcd(b, a mod b, x, y),稍加處理,即可求得方程ax+by=gcd(a,b)ax+by=gcd(a,\,b)的一組解x,yx,y

3.5 利用拓展歐幾里得算法求逆元

那麼問題來了,我們通過函數ex_gcd(a, b, x, y),求得gcd(a,b)gcd(a,b)(即最大公約數)的結果,以及一組方程解(x,y)(x,y),對求逆元有什麼作用?

1.根據逆元定義ax1(modb)a\,*\,x\,≡\,1\,(mod\,b)
當我們求 aamodbmod\,b 情況下的逆元時,假設逆元爲x,即
ax1(modb) ax\equiv1(mod\,b) 轉化等式:
ax1+by(  (by)(ax),axby) ax\equiv1+by(\,\,(b * y)|(a *x),即a * x是b * y的若干倍) 移項
ax+by1 ax+by\equiv1 則最小的 xx 即爲 aamodbmod\,b 情況下的一個逆元

2.由裴蜀定理:
ax+by=gcd(a,b)ax+by=gcd(a,\,b)
gcd(a,b)=1gcd(a,\,b)=1 時,由擴展歐幾里得函數ex_gcd(a, b, x, y)求得的方程解 xx 即爲我們所求的最小乘法逆元

3.因此下面代碼中的標記4得到解釋:

//拓展歐幾里得算法
ll ex_gcd(ll a, ll b, ll &x, ll &y){
	//標記1
	if(b == 0){
		x = 1, y = 0;
		return a;
	}
	ll d = ex_gcd(b, a % b, x, y);
	ll temp = y;
	//標記2
	y = x - (a / b) * y;
	x = temp;
	//標記3
	return d;
}

//求a在mod下的逆元x
ll getInv(ll a, ll mod){
	ll x, y;
	ll d = ex_gcd(a, mod, x, y);
	//標記4
	return d == 1 ? (x + mod) % mod : -1;
}
  • 當擴展歐幾里得函數ex_gcd(a, b, x, y)返回的最大公約數 d=1d = 1 時,xx 即爲 amodba\,mod\,b 下的最小乘法逆元,(x + mod) % mod是爲了將 xx 調整到 0 ~ (b - 1)的範圍中。
  • 當函數的返回值 d1d\neq 1 時,即說明逆元不存在,返回 1-1
  • 拓展歐幾里得求逆元的時間複雜度O(logn)O(logn)
  • 適用範圍:只要存在逆元即可求,適用於個數不多但是mod很大的時候,也是最常見的一種求逆元的方法。

4 費馬小定理

4.1 定義

如果 pp 是一個質數,且整數 aa 不是 pp 的倍數,則有:ap11(modp)a^{p-1}\equiv1(mod\,p)

  • 由公式得:
    ap2a1(modp) a^{p-2}\,*\,a\equiv1(mod\,p)
    因此 ap2a^{p-2} 即爲 aamodpmod\,p 意義下的逆元。
  • ap2a^{p-2} 可用快速冪求解(關於快速冪原理可在此博客瞭解:快速冪 & 快速乘原理講解(模板)

4.2 模板

//快速冪
ll ksm(ll a, ll p, ll mod) {
    ll ans = 1, base = a % mod;
    while(p) {
		if(p & 1) {
		    ans = (ans * base) % mod;
		}
		base = (base * base) % mod;
		p >>= 1;
    }
    return ans;
}
//求逆元
ll getInv(ll a, ll mod){
	return ksm(a, p - 2, mod);
}
  • 費馬小定理求逆元的時間複雜度O(logmod)O(logmod)
  • 適用範圍:根據費馬小定理,modpmod\,p質數時適用。並且比擴展歐幾里得算法好寫一些。

5 歐拉定理

歐拉定理(也稱費馬-歐拉定理),是一個關於同餘的性質。歐拉定理表明,若 p,ap,\,a 爲正整數,且 p,ap,\,a 互質,則:
aφ(p)1(mod p) a^{\varphi (p)}\equiv1(mod\ p)

  • 由公式得:
    aφ(p)1a1(modp) a^{\varphi (p)-1}\,*\,a\equiv1(mod\,p)
    因此 aφ(p)1a^{\varphi (p)-1} 即爲 aamodpmod\,p 意義下的逆元。
  • 可以看到,費馬小定理是歐拉定理的特例,當 pp 爲質數時,φ(p)=p1\varphi (p)=\,p-1φ(p)1=p2\varphi (p)-1=\,p-2
  • 歐拉定理比費馬小定理適用範圍更廣,因爲模數 pp 可以不是質數。
  • 一般很少有人直接用歐幾里得求逆元(偷個懶,板子就不貼上來了)

6 RSA公鑰密碼經典例題

說了這麼多廢話 (正經話),終於可以開始做題了。

6.1 題目描述

    RSA是一種經典的加密算法。它的基本加密過程如下。
    首先生成兩個質數 p,qp, q,令 n=pqn = p * q,設 dd(p1)(q1)(p - 1) * (q - 1) 互質,則可找到 ee 使得 ded * e(p1)(q1)(p - 1) * (q - 1) 的餘數爲 1。
    n,d,en, d, e 組成了私鑰,n,dn, d 組成了公鑰。
    當使用公鑰加密一個整數 XX 時(小於 nn ),計算 C=XdmodnC = X^{d}\,mod\,n ,則 CC 是加密後的密文。
    當收到密文 CC 時,可使用私鑰解開,計算公式爲 X=CemodnX=C^{e}\,mod\,n
    例如,當 p=5,q=11,d=3p = 5, q = 11, d = 3 時,n=55,e=27n = 55,\,e = 27
    若加密數字2424,得243mod55=1924^{3}\,mod\,55= 19.
    解密數字1919,得1927mod55=2419^{27}\,mod\,55= 24.
    現在你知道公鑰中 n=1001733993063167141,d=212353n = 1001733993063167141, \,d = 212353,同時你截獲了別人發送的密文C=20190324C = 20190324,請問,原文是多少?

6.2 分析

  • 由題目已知n,d,Cn,\,d,\,C\,,根據解密公式 X=CemodnX=C^{e}\,mod\,n,只需求得私鑰 ee ,即可解得原文 XX
  • 根據題目信息:dd(p1)(q1)(p - 1) * (q - 1) 互質,則可找到 ee 使得 ded * e(p1)(q1)(p - 1) * (q - 1) 的餘數爲 1,翻譯成 人話(公式)就是:de1(mod  (p1)(q1)) d\,*\,e\equiv1(mod\,\,(p - 1) * (q - 1)\,)
  • k=(p1)(q1)k = (p - 1) * (q - 1),即:de1(modk) d\,*\,e\equiv1(mod\,k\,)
  • 由上述公式可得 ee 即爲 ddmodkmod\,k意思下的乘法逆元。在這裏我們用拓展歐幾里得算法求解。
  • 最後一個問題:關於 kk(p1)(q1)(p - 1) * (q - 1)應該如何求解。這裏用到質因數分解,由題目可知 n=pqn = p * q,且 p,qp,\,q 均爲質數。我們先找到一個小於 nn 的質數 pp,再用 n/pn / p 即可得到 qq

6.3 題解

#include <iostream>
#include <cstdio>
using namespace std;

typedef long long ll;

ll n = 1001733993063167141, d = 212353, c = 20190324;

//判斷質數 
ll isPrime(ll x){
	for(ll i = 2;i <= x;i++){
		if(x % i == 0){
			return i;
		}
	}
}

//擴展歐幾里得算法 
ll ex_gcd(ll a, ll b, ll &x, ll &y){
	if(b == 0){
		x = 1, y = 0;
		return a;
	}
	ll d = ex_gcd(b, a % b, x, y);
	ll temp = y;
	y = x - (a / b) * y;
	x = temp;
	return d;
}

//求a在mod下的乘法逆元x
ll getInv(ll a, ll mod){
	ll x, y;
	ll d = ex_gcd(a, mod, x, y);
	return d == 1 ? (x + mod) % mod : -1;
}

//快速乘 
ll ksc(ll a, ll b, ll mod) {
    ll ans = 0;
    while(b) {
		if(b & 1) {
	    	ans = (ans + a) % mod;
		}
		a = (a + a) % mod;
		b >>= 1;
    }
    return ans;
}

//快速冪 
ll ksm(ll a, ll b, ll mod) {
    ll ans = 1, base = a;
    while(b) {
		if(b & 1) {
	    	ans = ksc(ans, base, mod) % mod;
		}
		base = ksc(base, base, mod) % mod;
		b >>= 1;
    }
    return ans;
}

int main(){
	ll p = isPrime(n), q = n / p, k = (p - 1) * (q - 1);
	ll e = getInv(d, k);
//	printf("e is %lld\n", e);
	ll x = ksm(c, e, n);
	printf("%lld\n", x);
	return 0;
}

運算結果:
在這裏插入圖片描述

  • 答案:579706994112328949
  • RSA是非對稱加密算法,基於大數分解,題目只是模擬了一下RSA解密的過程,可以看到當 n=1001733993063167141n = 1001733993063167141 時,我的電腦跑出結果就花了十幾秒。而真正RSA密碼中,nn 一般而言可達到 102420481024\sim2048 比特,普通的計算機跑出結果要十年左右,量子計算機需一週。

參考博客:歐幾里德算法與擴展歐幾里德算法

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