再帰アルゴリズムについては既に,「再帰アルゴリズムについて」で,またユークリッドの互除法 についても,「ユークリッドの互除法」,「拡張ユークリッドの互除法」で説明しました。 ここでは,再帰アルゴリズムを使って,ユークリッドの互除法を実現してみましょう。 再帰アルゴリズムはプログラミングでの基本的な技法のひとつで,その特徴を良く理解することは重要です。
再帰アルゴリズムで実現できるものは適当な書き直しで,非再帰にすることが可能ですから,再帰アルゴリズムが絶対に必要という訳ではありません。また一般的に,再帰版より,非再帰版の方が高速に動作しましから,高性能なプログラムを作り出すという技法でもありません。それどころか,使う状況を誤ると,とても非効率なプログラムになって しまうこともあります。 (例えば,フィボナッチ数を計算する再帰プログラムは使ってはいけない例でした。これについては,,「再帰アルゴリズムについて」で詳しく説明しました。)
このように見ていくと,再帰は必要ないと思うかもしれませんが,実際は決してそうではなく,再帰アルゴリズムは重要な技法です。では,その利点はどのようなものでしょうか。それは
ということです。プログラムで最も重要なこととして,「分かりやすい」ということがありました。ですjから,再帰アルゴリズムは,これを実現する,重要な技法の1つといえます。
ユークリッドの互除法は2つの自然数 a, b の最大公約数を効率的に計算する方法でした。 これを実現するプログラムとしては,普通は次のようなものがあげられます。次は高校の数学の教科書「数B」に載っていたものです。
これはこれで良いのですが,手計算で行うユークリッドの互除法と少し雰囲気が違います。 ユークリッドの互除法は,例えば, GCD(13,5) を計算するのに, 13 = 2*5 + 3 これに対処する方法としては,
または,
という方法が考えられます。上のプログラムは,後者の方法「変数の入れ替え」を行っています。 While・・・Wend の中で,a = b, b = r を行うのは変数の置き換えです。 このように見たとき,上のプログラムが十分に分かりやすいということならば,それまでの話です。しかし,変数の再利用は,プログラミングでの1つの技法ですので,それを利用することは無意味ではありませんが,ユークリッドの互除法の理解とは一応別の問題です。
次に再帰を使って,最大公約数を計算する方法を考えて見ましょう。実は,ユークリッドの互除法による最大公約数の計算は次の原理に拠っています。
さらに, a > 0, b=0 の場合は,直ちに,GCD(a, 0) = a です。 これらのことを注意して,最大公約数を計算する関数 GCDR を再帰を使って書くと,次のようになります。
Function を使うので,プログラミング技法としては,「数B」にあるプログラムよりも高級ですが,プログラムの中身自体は,極めて明快です。
この例ですと,再帰の効果の例としては余り説得力が無いかもしれませんが,次はもう少し効果的です。
ユークリッドの互除法の応用として,「拡張ユークリッド互除法」がありました。それは,
ものでした。そしてそれを計算するプログラムとして,
を挙げました。 ここでは,このプログラムの再帰版を作ってみましょう。
まず,ユークリッドの互除法の証明の過程を振り返って見ましょう。 便宜上 x=r0,y=r1と置きます。このとき,GCD(x,y)=r=rnは次の式で与えられます。 (0) r0=q1*r1+r2, 0< r2 <r1 拡張ユークリッドの互除法は,この過程を下から逆に辿り,求める結果を得るものでした。ここでは再帰を使用することとして,次のように考えます。
ここで,rn は最大公約数で固定です。s' と t' から s と t を求めるのは,簡単です。実際, ri = qi+1 ri+1 + ri+2 から, ri+2 = ri - qi+1 ri+1 として,これを s'ri+1 + t' ri+2 = rn に代入して整理すると,s = t' および, t = s' - t' qi+1 が得られます。また,明らかに, i = n のときは, rn+1 = 0 ですから, s = 1, t = 0, として, s rn + t rn+1 = rn が成立します。 従って,この過程をプログラムにすると,
となります。再帰版のほうが大分簡単です。
最後に,再帰を使ったプログラムの効率について考えて見ます。これらの再帰版のプログラムで,再帰的に自分を呼ぶ回数は,ユークリッドの互除法のステップの回数と同じです。従って,呼ぶ回数は非常に少数で,非再帰版に比べて計算効率は落ちません。非再帰版に比べて多少余計に内部的なメモリーを使用するだけで,プログラム全体がむしろ短いですから速度は落ちません。 まとめると, ユークリッドの互除法,拡張ユークリッドの互除法の再帰版は 非再帰版と同様に高速に動作します! |