inv gcd
AtCoder Library の中の internal_math.hpp
というファイルを開くと, inv_gcd
という名前の関数があります. 2 つの整数 a , b を受け取って, 2 数の最大公約数 g=gcd(a,b) と, xa≡gmodb なる x (ただし 0≤x<gb)を返します.
この記事では,この関数がどのように実装されているか考えます.
手計算
まず, a=100 , b=529 とした場合を手計算してみます.一般的に知られているやり方は,
5291002913÷÷÷÷100=529=313=23=4⋯⋯⋯⋯291331
を変形して
291331====5291002913−5×−3×−2×−4×10029133
とした上で,
1=1×13=1×13=(−4)×29=(−4)×29=(−4)×29=9×100=9×100=9×100=(−31)×529=(−31)×529+(−4)×3+(−4)×(29−2×13)+(1−(−4)×2)×13+9×13+9×(100−3×29)+(−4−9×3)×29+(−31)×29+(−31)×(529−5×100)+(9−(−31)×5)×100+164×100
から 164×100≡1mod529 を得るものでしょう.これは,
1=x1×13+x2×3=x2×29+x3×13=x3×100+x4×29=x4×529+x5×100
としたときに
x1x2x3x4x5=1=−4=x1−x2×2=x2−x3×3=x3−x4×5
という計算で x5 が求められるということを意味します.さらに x0=0 とすると
x0x1x2x3x4x5=0=1=x0−x1×4=x1−x2×2=x2−x3×3=x3−x4×5
となります.
一般化
以上の議論を全て文字に置き換えると, a0=b , a1=a として互除法を
a0÷a1a1÷a2a2÷a3an−1÷an=q0⋯a2=q1⋯a3=q2⋯a4⋮=qn−1⋯0
とした上で,
x0x1x2x3xn=0=1=x0−x1×qn−2=x1−x2×qn−3⋮=xn−2−xn−1×q0
としたときの xn が求めたかったものでした.
しかし,互除法では q0,q1,q2,… の順番に求まるのに対し, x0,x1,x2,… を求めるときは qn−2,qn−3,qn−4,… の順番で使うので,このままだと各 qi の値を保存しておかなければなりません.これを避けるため,計算順序を変えることを考えます.
行列表記
xi=xi−2−xi−1×qn−i
は,行列を用いると
(xi−1xi)=(011−qn−i)(xi−2xi−1)
と書くことができます.よって, xn を求める段階は
(xn−1xn)(xn−2xn−1)⋮(x2x3)(x1x2)=(011−q0)(xn−2xn−1)=(011−q1)(xn−3xn−2)=(011−qn−3)(x1x2)=(011−qn−2)(x0x1)=(011−qn−2)(01)
と書き換えることができます.これを一本にすると
(xn−1xn)=(011−q0)(011−q1)⋯(011−qn−2)(01)
となり, q0,q1,q2,… の順に使って計算することができるようになりました.
変形
行列を左から順に計算すると,
A0A1A2⋮An−1(xn−1xn)=(1001)=A0(011−q0)=A1(011−q1)=An−2(011−qn−2)=An−1(01)
となります.
行列 (xzyw) に行列 (011−qi) を右からかけると (ywx−qiyz−qiw) となり,上の行( x y )と下の行( z w )は独立しています.よって,下の行のみに着目することで次のように書き換えることができます.
(z0,w0)(z1,w1)(z2,w2)⋮(zn−1,wn−1)xn=(0,1)=(w0,z0−q0w0)=(w1,z1−q1w1)=(wn−2,zn−2−qn−2wn−2)=wn−1
こうして,互除法を行いながら (z,w) を更新することで xn が得られました.
確認
最初の a=100 , b=529 の場合について計算して確かめてみます.
- (z0,w0)=(0,1)
-
529÷100 の商は 5 なので (z1,w1)=(w0,z0−5w0)=(1,−5)
-
100÷29 の商は 3 なので (z2,w2)=(w1,z1−3w1)=(−5,16)
-
29÷13 の商は 2 なので (z3,w3)=(w2,z2−2w2)=(16,−37)
-
13÷3 の商は 4 なので (z4,w4)=(w3,z3−4w3)=(−37,164)
ちゃんと,最後に w4 の値が 164 になっています.
Discussion