在數論和演算法競賽中,我們經常需要在模運算 (Modular Arithmetic) 的環境下進行除法。然而,模運算中沒有直接的除法操作。取而代之的是,我們需要找到一個數的「模乘法反元素」,並將除法轉換為乘法。

本文將介紹什麼是模乘法反元素,以及如何使用擴展歐幾里得算法 (Extended Euclidean Algorithm, ExtGcd) 來高效地找到它。

什麼是模乘法反元素?

對於兩個整數 am,如果存在一個整數 x 滿足以下同餘方程式:

a * x ≡ 1 (mod m)

那麼,我們稱 xa 在模 m 下的模乘法反元素 (Modular Multiplicative Inverse)。

有了反元素 x,當我們想計算 b / a (mod m) 時,就可以轉換為計算 b * x (mod m),從而實現模運算中的「除法」。

反元素的存在條件

並不是所有的 a 在模 m 下都有反元素。反元素存在的充分必要條件am 互質 (coprime),也就是說它們的最大公因數 (Greatest Common Divisor, GCD) 為 1。

gcd(a, m) = 1

如果 gcd(a, m) ≠ 1,那麼 a 在模 m 下的乘法反元素不存在。

擴展歐幾里得算法 (ExtGcd)

擴展歐幾里得算法是歐幾里得算法的延伸。它不僅能計算出兩個整數 ab 的最大公因數 d,還能同時找到一對整數 xy,使得它們滿足貝祖定理 (Bézout’s identity)

a * x + b * y = gcd(a, b)

如何利用 ExtGcd 找反元素?

現在,讓我們把這個公式與模乘法反元素的需求聯繫起來。我們想找的反元素 x 滿足:

a * x ≡ 1 (mod m)

這等價於存在一個整數 k,使得:

a * x = 1 + k * m

移項後得到:

a * x - k * m = 1

y = -k,上式變為:

a * x + m * y = 1

這和擴展歐幾里得算法的結果 a * x + m * y = gcd(a, m) 非常相似!

am 互質時,gcd(a, m) = 1,此時擴展歐幾里得算法給出的 xy 正好滿足 a * x + m * y = 1

對這個等式兩邊取模 m

(a * x + m * y) mod m = 1 mod m
(a * x) mod m + (m * y) mod m = 1
a * x ≡ 1 (mod m)

瞧!擴展歐幾里得算法所求出的 x,正是 a 在模 m 下的乘法反元素。

需要注意的是,算法求出的 x 可能是負數。我們需要將其轉換為 [0, m-1] 範圍內的等價正整數,常用的方法是 (x % m + m) % m

Python 實作

以下是使用 Python 實作的擴展歐幾里得算法,並用它來求解模乘法反元素。

def ext_gcd(a, b):
    """
    擴展歐幾里得算法
    返回 (gcd, x, y) 使得 a*x + b*y = gcd(a, b)
    """
    if b == 0:
        return a, 1, 0
    else:
        gcd, x, y = ext_gcd(b, a % b)
        # a*x + b*y = gcd(a,b)
        # b*x' + (a%b)*y' = gcd(b, a%b)
        # a%b = a - floor(a/b)*b
        # b*x' + (a - floor(a/b)*b)*y' = gcd
        # a*y' + b*(x' - floor(a/b)*y') = gcd
        # So, x = y', y = x' - floor(a/b)*y'
        return gcd, y, x - (a // b) * y

def mod_inverse(a, m):
    """
    計算 a 在模 m 下的乘法反元素
    """
    gcd, x, y = ext_gcd(a, m)
    if gcd != 1:
        # 反元素不存在
        raise ValueError(f"Inverse does not exist for a={a}, m={m} as gcd is {gcd}")
    
    # 將 x 調整到 [0, m-1] 的範圍內
    return (x % m + m) % m

# --- 範例 ---
a = 7
m = 26

try:
    inv = mod_inverse(a, m)
    print(f"{a} 在模 {m} 下的反元素是: {inv}")
    print(f"驗證: ({a} * {inv}) % {m} = {(a * inv) % m}")
except ValueError as e:
    print(e)

# --- 另一個範例 ---
a = 4
m = 10

try:
    inv = mod_inverse(a, m)
    print(f"{a} 在模 {m} 下的反元素是: {inv}")
except ValueError as e:
    print(e)

總結

模乘法反元素是模運算中不可或缺的工具,它讓我們能將除法問題轉化為乘法問題。透過擴展歐幾里得算法,我們可以在 am 互質的前提下,快速且有效地計算出反元素。這個算法不僅在理論上優雅,在實際應用(如密碼學、演算法競賽)中也扮演著關鍵角色。