目次

ユークリッド互除法

2数の最大公約数を求める。

拡張ユークリッド互除法

2数 $x,y$ の最大公約数 $gcd(x,y)$ とともに、

$$ ax + by = gcd(x,y) $$

となる $(a,b)$ の組の1つも見つける。

組の1つが見つかれば、後は $u=\frac{x}{gcd(x,y)},v=\frac{y}{gcd(x,y)}$ として

$$ ..., (a-2v,b+2u), (a-v,b+u), (a,b), (a+v,b-u), (a+2v,b-2u), ... $$

が過不足なく全て等式を満たす組となる。

# 引用(もといパクリ)
# Python で RSA 公開鍵暗号をなぞってみる - CAMPHOR- Tech Blog
# https://tech.camph.net/rsa-public-key-encryption/

def ex_euclid(x, y):
    c0, c1 = x, y
    a0, a1 = 1, 0
    b0, b1 = 0, 1

    while c1 != 0:
        m = c0 % c1
        q = c0 // c1

        c0, c1 = c1, m
        a0, a1 = a1, (a0 - q * a1)
        b0, b1 = b1, (b0 - q * b1)

    return c0, a0, b0

ユークリッドの互除法の調整

互除法とは直接関係ないが、一緒に使うことがある制約条件について。

$a,b$ が正という条件で全探索する時、$a$ を最小(最大でもいいが)にしておくとやりやすい。

まず、$c=gcd(x,y)$ として、$z$ が $c$ の倍数で無い場合、等式は不可能である。

次に、拡張ユークリッド互除法で $a'x+b'y=c$ となる $a',b'$ を求める。$w=\frac{z}{c}$ とすると、$a'xw+b'yw=cw=z$ となり、$(a,b)=(a'w, b'w)$ が答えの一つとなる。

その後、非負チェックと $a$ の最小化を行う。$a$ は $v=\frac{y}{c}$ 単位でしか増減できないので $a\%v$ が最小値となる。負の場合だが、pythonでは $a$ が負であっても $a\%v$ で正の最小値が求まる。

def exex_euclid(x,y):
    c, a, b = ex_euclid(x, y)
    w, m = divmod(z, c)
    
    # zがcの倍数でないなら等式は不可能
    if m != 0:
        return None
        
    u, v = x // c, y // c
    a, b = a * w, b * w

    # aを非負数の中で最小にする
    f, a = divmod(a, v)
    b += u * f
    
    # aを最小にしたのにbが負なら、ともに正の組は不可能
    if b < 0:
        return None

    return c, a, b, u, v