拡張ユークリッド互除法 (Extended Euclidean algorithm)

拡張ユークリッド互除法は,$a, b$を整数,$g=\gcd(a,b)$とするとき,次の1次不定方程式 \[ax + by = g\] の解の1つ$(x_0, y_0)$求めるアルゴリズムである。解の1つが得られれば,全ての解は \[x=x_0+k\cdot\frac{b}{g},\ y=y_0-k\cdot\frac{a}{g}, \ \ \ \mbox{($k$は整数)}\] である。

また,不定方程式$ax + by = c$は$c$が$g$の倍数のときに限り解を持つ。$c=dg$,$ax + by = g$の解の1つを$(x_0,y_0)$とすれば,$ax+by=c$の解は, \[x=dx_0+k\cdot\frac{b}{g},\ y=dy_0-k\cdot\frac{a}{g}, \ \ \ \mbox{($k$は整数)}\] となる。

モジュラ逆数を求める際に利用できる。

説明

下のサイトなどを参照のこと

例題

次の「油わけ算」:

5合のマスと7合のマスを使って1合の油を量れ
を解くプログラムを作れ。

5合,7合のマスを使う回数をそれぞれ$x,y$とする。$\gcd(5,7)=1$なので$5x+7y=\gcd(5,7)$を解けばよい。解が正なら量って加え,負の値なら量って捨てることを意味する。

サンプルコード (Java)

class Main {
    /**
     *  ax + by = gcd(a,b)を解く
     */
    static long[] xgcd(long a, long b) {
        long r0 = a, r1 = b;
        long x0 = 1, x1 = 0;
        long y0 = 0, y1 = 1;
        while (r1 != 0) {
            long q = r0 / r1;
            long u = r1;
            r1 = r0 - q * r1;
            r0 = u;
            long v = x1;
            x1 = x0 - q * x1;
            x0 = v;
            long w = y1;
            y1 = y0 - q * y1;
            y0 = w;
        }
        // gcd(a,b)が必要ならr0を返せばよい
        return new long[]{x0, y0};
    }

    public static void main(String args[]) {
        int a = 5, b = 7;
        long[] r = xgcd(a, b);
        System.out.printf("%d合のマスで%d回すくう\n", a, r[0]);
        System.out.printf("%d合のマスで%d回すくう\n", b, r[1]);
    }
}

サンプルコード (Python3)

def xgcd(a, b):
    """ax + by = gcd(a,b)を解く
    """
    r0, r1 = a, b
    x0, x1 = 1, 0
    y0, y1 = 0, 1

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

    # gcd(a,b)が必要ならr0も返せばよい
    return (x0, y0)


a = 5
b = 7
r = xgcd(a, b)
print("%d合のマスで%d回すくう" % (a, r[0]))
print("%d合のマスで%d回すくう" % (b, r[1]))