モジュラ逆数 (modular multiplicative inverse)

$a, m$を整数とするとき, \[ax \equiv 1 \pmod{m}\] となる$x$を$a$の(法$m$の)モジュラ逆数とよび,$a^{-1}$で表す。

$a$のモジュラ逆数が存在する必要十分条件は$\gcd(a,m)=1$である。

モジュラ逆数の計算

$m$が素数の場合

$p$を素数とし,法$p$でのモジュラ逆数を求める場合,フェルマーの小定理を利用できる。

フェルマーの小定理によれば$a\not\equiv 0\pmod{p}$のとき \[a^{p-1}\equiv 1\pmod{p}\] が成り立つので, \[a^{-1}\equiv a^{p-2}\pmod{p}\] である。

$a^{p-2}$は繰り返し二乗法を用いれば$O(\log p)$回程度の乗除算で求めることができる。

Pythonでは法$m$での$a^n$を,組込み関数powを使って,pow(a, n, m)で 計算できるので,

    inva = pow(a, p-2, p)
とするだけでよい。

$m$が合成数の場合(素数でもよい)

$a,m$は互いに素とする。$m$が素数でない場合は,オイラーの定理から \[a^{\phi(m)}\equiv 1\pmod{p}\] が成り立つので, \[a^{-1}\equiv a^{\phi(m)-1}\pmod{p}\] である。$\phi(m)$があらかじめ分かっている場合はこの方法でもよいが,一般に$\phi(m)$を求めるには$m$の素因数分解する程度の時間がかかるので,この方法は効率がよくない。

通常,モジュラ逆数を求める場合は,拡張ユークリッド互除法を利用して \[ax + my = 1\] の解を見つければ \[ax \equiv 1 - my \equiv 1 \pmod{m}\] なので,$x$が$a^{-1}$である。この方法でも$O(\log m)$回程度の乗除算が必要であるが,オイラーの定理を利用した繰り返し二乗法よりも平均的には高速である。

例題

法10000で,$7^{-1}$を求めよ。

$\phi(m)=2^5\cdot 5^3=4000$であるから,$7^{3999}$を求めてもよいが,拡張ユークリッド互除法を使う。

サンプルコード (Java)

class Main {
    /**
     *  法m における a の逆元
     */
    static long inv(long a, long m) {
        long r1 = a, r0 = m;
        long x1 = 1, x0 = 0;
        while (r1 != 0) {
            long q = r0 / r1;
            long v = r1;
            r1 = r0 - q * r1;
            r0 = v;
            long w = x1;
            x1 = x0 - q * x1;
            x0 = w;
        }

        // ここでもし r_0 != 1 ならエラー処理をすべき

        if (x0 < 0) {
            x0 += m;
        }
        return x0;
    }

    public static void main(String[] args) {
        System.out.println(inv(7, 10000));
    }
}

サンプルコード (Python3)

def inv(a, m):
    """法 m における a の逆元
    """

    r0, r1 = m, a
    x0, x1 = 0, 1

    while r1 != 0:
        q = r0 // r1
        r0, r1 = r1, r0 - q * r1
        x0, x1 = x1, x0 - q * x1

    # ここでもし r_0 != 1 ならエラー処理をすべき

    if x0 < 0:
        x0 += m

    return x0


print(inv(7,10000))
print(pow(7,4000-1,10000)) #これもOK