實現模乘法逆元算法
https://github.com/SKPrimin/HomeWork/tree/main/Cryptology/02gcd
一、實驗目的
通過本實驗使學生掌握最大公因子算法的實現、同余類中元素的乘法逆元的求解。
二、實驗原理
本實驗的准備知識包括最大公約數、模運算及其基本性質、互素等概念。
-
最大公約數:a和b的最大公約數是能夠同時整除a和b的最大正整數,記為:gcd(a,b)或(a,b)。
-
互素的(既約的):滿足 gcd(a,b)=1 的 a 和 b。
-
同余(模運算):設整數 a,b,n(n≠0),如果 a-b 是 n 的整數倍(正的或負的),我們就說 a≡b(mod n),讀作:a 同余於 b 模 n。
-
歐幾里得算法:又稱輾轉相除法,基於定理 gcd(a,b)=gcd(b,a mod b) (a>b)
-
擴展的歐幾里得算法:對於不完全為 0 的非負整數 a,b,gcd(a,b)表示 a,b 的最大公約數,必然存在整數對 x,y,使得 gcd(a,b) = ax+by。
-
乘法逆元素:假設 gcd(a,n)=1,則存在整數 s,t,使得 as+nt=1(由擴展的歐幾里得算法可知),則 as≡1(mod n),因此 s 是 a(mod n)的乘法逆元素。且有: a − 1 a^{-1} a−1 ≡s(mod n)。
三、實驗內容
實現以下算法,加深掌握模運算的性質
-
歐幾里得算法
計算 gcd(23456,987654)
-
擴展的歐幾里得算法
計算 8787 (mod 91919)的乘法逆元素
實驗具體實現
python
如果我們使用python,由於有gmpy2
庫的存在,自然是三句話讓python給為寫了十八個實驗
gmpy2
實現
gmpy2.mpz(n)
初始化一個大整數gmpy2.invert(m,phi)
求 m o d ( p h i ) mod (phi) mod(phi)的逆元pow(m,e,n)
求 c d m o d n c^d mod n cdmodngmpy2.is_prime(n)
素數判斷gmpy2.gcd(a,b)
歐幾里得算法,最大公約數gmpy2.gcdext(a,b)
擴展歐幾里得算法gmpy2.iroot(x,n)
x開n次根, x n \sqrt[n]{x} nx
import gmpy2
a, b = map(int, input("請輸入a,b").split())
print(gmpy2.gcd(a, b))
print(gmpy2.gcdext(a, b))
m,phi= map(int, input("請輸入m,phi").split())
print(gmpy2.invert(m,phi))
運行結果為
請輸入a,b>? 23456 987654
2
(mpz(2), mpz(-3158), mpz(75))
請輸入m,phi>? 8787 91919
71374
歐幾里得拓展算法返回一個三元素元組tuple (g,s,t)
其中g == gcd(a,b) and g == a*s + b*t
當然這只是調用庫,並不能體現算法,但由於gmpy2的源代碼無法步入,這里對歐幾里得算法做了推算,運行結果也是正確的
def gcdext(a: int, b: int) -> (int, int, int):
# 擴展歐幾里得算法 tuple (g,s,t)
s, t, x, y = 1, 0, 0, 1 # 初始化s,t,x2,y2
while b:
q, r = divmod(a, b)
a, b = b, r # 求最大公約數
s, t, x, y = x, y, s - q * x, t - q * y # 輾轉相除
# 返回元組(g,s,t),使 g == a*s + b*t
return a, s, t
def gcd(a: int, b: int):
# 歐幾里得算法求最大公約數
while a != 0:
a, b = b % a, a
return b
def invert(m:int, phi:int):
# 求逆元,實際上調用拓展歐幾里得函數
_, mi, _ = gcdext(m, phi)
return mi if mi > 0 else mi + phi
if __name__ == "__main__":
a, b = map(int, input("請輸入a,b").split())
print(gcd(a, b))
print(gcdext(a, b))
m, phi = map(int, input("請輸入m,phi").split())
print(invert(m, phi))
java
public class Gcd {
int a, b;
private int g, s = 1, t = 0, m, n; //此處使用私有變量,保護g,s,t
public Gcd() { //空構造器
}
public Gcd(int a, int b) { //全參構造器
this.a = a;
this.b = b;
m = a; //存儲a,b以備不時之需
n = b;
}
@Override
public String toString() {
return "Gcd{" + "a=" + m + ", b=" + n + ", g=" + g + ", s=" + s + ", t=" + t + '}';
}
static int getG(int a, int b) { //本處使用了靜態類,即可以不用創建實例,直接 Gcd.getG(a,b)調用
if (b == 0) { //mod(a,b) = 0,則 此時的a就是最大公約數
return a;
}
int r = a % b; // 輾轉相除法
return getG(b, r); //遞歸
// return b==0?a:getG(b,a%b); // 三元運算符版本,一行解決
}
void getGst() {
int x = 0, y = 1, q, r, sy, ty;
while (b != 0) { //輾轉相除
q = a / b; // 將 ab的整數倍存儲起來
r = a % b;// 將 ab的余數存儲起來
a = b;
b = r;
sy = s; //將 s,t原始值存儲起來
ty = t;
s = x; // 輾轉交換
t = y;
x = sy - q * x;
y = ty - q * y;
}
g = a;
}
public int getS() { //s即逆元
return s > 0 ? s : s + m; //如果算出的s小於0,就變為正數
}
}
class Test {
public static void main(String[] args) {
Gcd gcd = new Gcd(23456, 987654);
gcd.getGst();
System.out.println(gcd.toString());
//使用靜態類可以直接調用
System.out.println(Gcd.getG(23456, 987654));
Gcd gcd2 = new Gcd(8787, 91919);
gcd2.getGst();
System.out.println(gcd2.getS());
}
}
運行結果
Gcd{a=23456, b=987654, g=2, s=-3158, t=75}
2
71374