ま、最大公約数を出す計算です。知ってる人にはどうってことない話なんでしょうが、この計算のアルゴリズムのForth版が、あまりに簡単過ぎて鼻水が垂れそうになったので、一応、紹介しとこうかなとおもいました。理屈を説明する方がずっと面倒なので、まずコードを書いときます。二つの数値の最大公約数を得るワード"GCM"の定義です。
: GCM ( n1 n2 -- gcm ) BEGIN tuck mod dup NUNTIL drop ;
おしまい。どうぢゃ、われぇ、どっからでも、かかってこんかい!(何弁? (||_ _ ) ええと、内容は6つのワードでできています。単純ですねえ。
コードの動作から説明しますと、BEGIN-NUNTIL
ループを使っています。これはBEGIN-UNTIL
ループと対になるもので、"NUNTIL"のところで、スタックの一番上の値を消費して、それが"0"(FALSE)なら
ループを抜け、そうでないなら"BEGIN"の直後に戻ってもう一回コードを実行します。
"tuck"はスタック操作ワードで、スタックの一番上の値をコピーした上で、上から三番目の位置にそれを押し込みます。つまり、
TUCK ( x1 x2 -- x2 x1 x2 )
ということですね。
次の"mod"は、スタックの値の二つを取って、割り算の余りを残します。つまり、
MOD ( n1 n2 -- u ) \ uはn1をn2で割ったときの余り
ということです。
上の余りが0になるまで同じことを繰り返します。"NUNTIL"がスタック値を消費するので、それように"dup"で余りの値をもうひとつ複製しておきます。そして、0になったら
ループを抜けます。残っている0を"drop"で落として終わりです。手順から考えて、残る値は、余りが0になる一つ手前の余りということがわかります。
ユークリッド互除法
では、しょうがないので、ユークリッド互除法について、それが何で最大公約数になるのか説明します。正確なことを知りたい方は初等数論の教科書でもご覧下さい。
計算の仕方は、まあ、まさに上のコードと同じことをするわけですが、数式風に書いてみます。aとbに計算を適用するとして、a > bと仮定しましょう。
上のコードでは入力値の大小を問題にしていませんが、a < bのときには、最初の試行で順番が入れ替わります。まあ、少し考えれば簡単にわかります。
一応、どっちも正とします。すると、どんな組み合わせでも、
a = q0×b + r1 ここで 0 ≦ r1 < b
と書けて、この書き方は一種類であることがわかります。数値の大小関係を考えれば直観的には明らかですね。証明略です。
で、(a,b)の組を、今度は、(b,r1)の組に替えて、同じ式を考えます。つまり、bをr1で割った余りr2を考えるわけです。
b = q1×r1 + r2 ... 0 ≦ r2 < r1
そして、次は(r1,r2)の組に... というようにこれを続けていく。
r1 = q2×r2 + r3
r2 = q3×r3 + r4
....
rk-1 = qk×rk + rk+1
.....
すると、この数式の系列はどこかで必ず、
rn-1 = qn×rn ... どの数値も正
というところに行き着く。つまり、余りがどこかで0になる(いってみれば、rn+1=0ですね、ここでは)。このとき、rnがaとbの最大公約数になっている、というのが、ユークリッド互除法です。
ご存知の方は別として、初めてきくと「そううまくいくかよ」という感じですね。いくんです。
まず、どっかで余りが0になるという点。初めの方に書いた余り系列の関係をみると、
(b >) r1 > r2 > r3 > .. > rk-1 > rk > ...
と、必ず減少していかなければならないことがわかります。ところが、余りには0以上の整数という縛りがありますし、最初のbは有限の数値ですから、必ずどこかで0になるはずなわけです。有理数とか実数とか稠密な数ではそうも行きませんが、整数では必ずそれがいえるのですね。
そして、rnが最大公約数になるという点。ます、公約数であることですが、最後の式の形から、rnはrn-1を割り切る、つまり、rn-1の約数であることがわかります。逆に言うと、rn-1はrnの倍数です。ここで一つ遡った式を考えましょう。
rn-2 = qn-1×rn-1 + rn
すると、右辺のrn-1もrnも、ともにrnの倍数なわけですから、左辺のrn-2もrnの倍数、つまり、rnはrn-2の約数でもあるわけです。こういう風に、どんどん遡っていけますので、式の系列の始めの方には、a,bとも左辺であったことがあるわけで、こうして、rnがa,bの公約数であることがわかるわけです。
次に、「最大」という点。例えば、ええと、Cがa,bの公約数だったとしますね。つまり、aもbもCの倍数と。すると、最初の式
a = q0×b + r1
を
a - q0×b = r1
と変形すれば、 r1もCの倍数でなければならないことが、丸わかりですね。左辺のaもbもCの倍数なので、左辺全体がCの倍数、ということは、それと等しい右辺r1は当然Cの倍数でしょう。
またこれをどんどん、今度は式の系列の進行方向に続けていくと、 rnもCの倍数でなければならないことになります。つまり、 rnはCで割り切れなければいけないのです。だから、 rnは最大公約数なのです。だって、 rnはどんな公約数ででも割り切れなきゃいけないんですよ。だから、最大公約数ででも割り切れなきゃいけないはずです。 rnが最大公約数より小さいってなら、自分より大きい数じゃあ割り切れるはずないですね。最大公約数が rnより小さいなら、 rnだって公約数なんだから最大といえないじゃないですか。だから両者は等しいんです。
これで、最大公約数なるものの存在まで証明しちゃったわけですね。
今回は初等数学教室みたいだったですね。むむむ。
オマケ
おまけですが、任意の個数(無限はダメよ)の組の最大公約数を計算するコードも書いておきましょう。順々に適用していくだけですから、単純です。最大公約数を取りたい数値を並べ、最後(トップスタック)に項数を表す数値を置く、という形式にしときました。
: mGCM ( n1 n2 ... nk k -- gcm )
1- FOR BEGIN tuck mod dup NUNTIL drop NEXT ;
ま、これだけなんですがね。
最終更新:2019年07月04日 23:50