密碼學之模乘法逆元算法 歐幾里得拓展算法 逆元 python java實現


實現模乘法逆元算法

https://github.com/SKPrimin/HomeWork/tree/main/Cryptology/02gcd

一、實驗目的

通過本實驗使學生掌握最大公因子算法的實現、同余類中元素的乘法逆元的求解。

二、實驗原理

本實驗的准備知識包括最大公約數、模運算及其基本性質、互素等概念。

  • 最大公約數:ab的最大公約數是能夠同時整除ab的最大正整數,記為:gcd(a,b)或(a,b)。

  • 互素的(既約的):滿足 gcd(a,b)=1 的 ab

  • 同余(模運算):設整數 abn(n≠0),如果 a-bn 的整數倍(正的或負的),我們就說 ab(mod n),讀作:a 同余於 bn

在這里插入圖片描述

  • 歐幾里得算法:又稱輾轉相除法,基於定理 gcd(a,b)=gcd(b,a mod b) (a>b)

  • 擴展的歐幾里得算法:對於不完全為 0 的非負整數 ab,gcd(a,b)表示 ab 的最大公約數,必然存在整數對 xy,使得 gcd(a,b) = ax+by

  • 乘法逆元素:假設 gcd(a,n)=1,則存在整數 st,使得 as+nt=1(由擴展的歐幾里得算法可知),則 as≡1(mod n),因此 sa(mod n)的乘法逆元素。且有: a − 1 a^{-1} a1s(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 cdmodn
  • gmpy2.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

在這里插入圖片描述


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM