先日の連分数の計算と拡張ユークリッド互除法って少し似てるなと思ったというメモ。(連分数の計算で次の項を求める時にやっている操作ってユークリッドの互除法そのものだし当然か)
拡張ユークリッド互除法というのは、 を満たす
を求める方法。
を
で割れば
(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))))
みたいに書けるけど他の処理系でも行ける?