1.6.3. 素数を法とする逆元

フェルマーの小定理

Fermat’s little theorem (Wikipedia)

\(p\) が素数ならば、拡張ユークリッドの互除法の代わりに、累乗 (繰り返し二乗法) により逆元を求めることができる。

累乗で逆元を求めるには、フェルマーの小定理

\[a^p \equiv a \mod p\]

を用いる。

逆元 \(a^{-1}\) が存在する (\(\gcd(p, n) = 1\) を満たす) とき、上式の両辺に $ a^{-1}$ を2回掛けると

\[a^{p-2} \equiv a^{-1}\]

となり、逆元は \(a^{p-2}\) で求まることがわかる。

実装

概要

繰り返し二乗法で冪乗 \(a^x \pmod p\) を計算する。 指数 \(x\) は負数でも良く、 \(a^{-1}\) で逆元を求める。
Pythonの組み込み関数 pow で上位機能が既に実装されているが、ここでは素数の法 \(p\) かつ \(\gcd(a, p) = 1\) の制約下で再実装した。

実装のポイント

逆元の存在判定は行わない実装とした。
逆元の存在判定が必要であれば、拡張ユークリッドの互除法を使うほうが良い。

また、指数部の処理にはフェルマーの小定理から得られる関係式

\[a^{x + (p-1)} \equiv a^x \pmod p\]

を用いた。

計算量

  • mod_pow(a, p) \(\mathcal{O}(\log{p})\)

コード

[1]:
def mod_pow(a: int, x: int, p: int) -> int:
    x %= (p-1)
    y = 1
    while x > 0:
        if x & 1 == 1:
            y *= a
            y %= p
        x >>= 1
        a *= a
        a %= p
    return y

使用例

[2]:
inv = mod_pow(7, -1, 1000000007)
print(inv)
print(7 * inv % 1000000007 == 1)
142857144
True