先日の連分数の計算と拡張ユークリッド互除法って少し似てるなと思ったというメモ。(連分数の計算で次の項を求める時にやっている操作ってユークリッドの互除法そのものだし当然か)
拡張ユークリッド互除法というのは、 を満たす
を求める方法。
を
で割れば
(a'とb'は互いに素)という形になる。
(以下、 の場合 swap(a, b)してから)
ユークリッドの互除法の各ステップが
のとき
という風に が求まる、というのが拡張ユークリッド互除法である。
ちなみに は連分数で
と表すことができる。
// extgcd.cc #include <cstdio> #include <cstdlib> int extgcd(int a, int b, int &x, int &y, int c=1, int d=0, int e=0, int f=1) { if (b == 0) { x = c; y = e; // dot([c,d; e,f], [1;0]) return a; } int q = a/b, r = a%b; return extgcd(b, r, x, y, d, c-q*d, f, e-q*f); // dot([c,d; e,f], [0,1; 1,-q]) } int main(int argc, char **argv) { if (argc == 3) { int a = atoi(argv[1]), b = atoi(argv[2]); int x, y; int g = extgcd(a, b, x, y); printf("gcd(%d,%d)=%d; x=%d,y=%d\n", a, b, g, x, y); } else { printf("usage: %s <a> <b>\n", argv[0]); } return 0; }
おまけ
in scheme
(define (extgcd a b) ; -> gcd(a,b), x, y (let loop ((a a) (b b) (c 1) (d 0) (e 0) (f 1)) (if (zero? b) (values a c e) (let ((q (quotient a b))) (loop b (remainder a b) d (- c (* q d)) f (- e (* q f))))))) (receive (g x y) (extgcd 4 3) (format #t "gcd(a,b)=~d; x=~d,y=~d\n" g x y)) ;; => gcd(a,b)=1; x=1,y=-1 (receive (g x y) (extgcd 48 15) (format #t "gcd(a,b)=~d; x=~d,y=~d\n" g x y)) ;; => gcd(a,b)=3; x=1,y=-3
↑商と剰余の計算をGaucheだと
(receive (q r) (quotient&remainder a b) (loop b r d (- c (* q d)) f (- e (* q f))))
みたいに書けるけど他の処理系でも行ける?