1)問題の分析──────────────────────────────1)
-
この問題で考えることはそんなにありませんね。とりあえず、入出力は標準入出力としておけば十分でしょう。あと必要なものは与えられた2数と、それらの商、余りです。
学生にこの手の問題を出すと、すぐメインルーチンで解こうとする人が多いのですが、あまりよくありません。こういう関数は後で使えるので、なるべく独立性を高くした方がいいのです。よって、2数を引数とし、その最大公約数を返す関数を作成することにします。(これは再帰版でも同じです)。
余談ですが、入出力については今回は(前回と違って)問題になりません。なぜなら、入出力に関係なく、与えられた2数の最大公約数を返す関数を作成すればいいからです。
詳しくは、後述します。
2)データとデータ構造──────────────────────────2)
-
データとしては、2数が与えられるので、これを受ける変数が必要です。それらの型としては普通の整数型で十分でしょう。あと、商、余りもあります。これらも整数型にしておきましょう。
3)大まかな流れ─────────────────────────────3)
-
大筋は簡単なので考えなくてもいいでしょう。
1)2数を入力する。
2)最大公約数を計算する。
3)結果を出力する。
こんな感じになります。えっ、何を当り前のことをって。いやこれで終りではなく、肝腎の最大公約数を計算する部分も考えましょう。二つの数が与えられたとき、その最大公約数を計算するにはどうすればいいでしょうか。(もちろんユークリッドの互除法を用いてです)これは少し考えてもらいましょう。(問題の例をよく見てください)
:
:
:
:
:
:
:
:
:
解答は次章で。
4)細かいアルゴリズムの決定───────────────────────4)
-
まず、メインルーチンの方からいきましょう。簡単ですが、前章を参考に少し考えてみてください。
:
:
:
:
:
:
:
:
:
では解答です。
最初はメインルーチンです。
まず、数の入力方法ですが、簡単にscanfを使いましょう。確かに前回、(またはご質問の解答で)scanf()は扱いが厄介なので使用は控えましょう、と書きましたが、この場合、逆にgetchar()で整数を入力する方が面倒なので、あえてscanf()を用いることにします。もし、ほかのものを使用する場合は、そこだけ書き換えても差し支えないでしょう。
次に最大公約数を計算する関数を呼び出し、その答えを変数にいれておきます。
最後に求めた答えを出力します。
次に、最大公約数を計算する関数ですが、どうなるでしょうか。(先の問題の例を見てください)
まず、とりあえず2数の余りを求めます。その余りが0なら割った答え(つまり商)が求める最大公約数です。もし0でないなら、割る数を余りで割り、その答えが0かどうか調べます。そして、余りが0になるまで繰り返します。この繰り返す部分をうまく作ればいいのです。手順としては
(1)aをbで割り、その余りをcとする。
(2)もしcが0なら(7)へ行く。
(3)a ← bとする。
(4)b ← cとする。
(5)a÷bを計算し、余りをcとする。
(6)(2)へ行く。
(7)bが答えである。
(ただし、a、bは与えられた整数でa>bとする。以降同じ)
こんな感じでしょうか。 (記号” ← ”は代入をあらわします。)
しかし、このアルゴリズムには、1つミスがあり、かつ冗長です。
ミスは”もしbが0ならどうするか”ということであり、冗長な部分は”手順の中で2回割算がでてくる”ということです。bの問題はif文などを用いて解決できますが、冗長な部分はどうでしょうか。
アルゴリズムによっては、このような冗長性はなくせませんが、今回はなくすことが出来ます。実はその方法を使うと先のミス”bが0の時”も同時に解決できます。さぁ、一体どうすればいいのでしょうか。少し考えてみてください。
:
:
:
:
:
:
:
:
:
では、解答です。
まず、bが0の時を考えてみましょう。この時、求める最大公約数はaになります。
よって、
(0)もしbが0ならaが答えである。
ということになります。
さて次に冗長性ですが、先の例の図とアルゴリズムをよく見てください。aにbを、bにcを代入していますね。ここがポイントになります。bにcを代入しているのですから、”cが0であるかないか”という問題はそのままbについていえることになります。するとaわるbを計算し、その余りをcとして、a ← b、b ← cとして先の(0)で比較すればいいことになります。よく分からない方は、もう一度よく考えてみてください。
結局、アルゴリズムは以下のようになります。
(0)もしbが0なら(5)へいく。
(1)aをbで割り、その余りをcとする。
(2)a ← bとする。
(3)b ← cとする。
(4)(0)へいく。
(5)aが求める答えである。
(ただし、a、bは与えられた整数でa>bとする。)
よろしいですか??
これを実際にプログラムに直すわけですが、繰り返しに関してはCでは3種類ありましたね。今回はどれを用いればよいでしょうか。そうです。while文ですね。他のものを使って書くことは出来ますが、あまり自然ではありません。
では前回同様、抽象言語を使ってアルゴリズムを書き直してみましょう。
while(bが0でないならば)
{
a÷bの余りをcとする。
a=b;
b=c;
}
こうなります。最後になりましたが、関数はint型を返し、同じく、int型の引数を二つとります。
では、これらを基にプログラムを書いてみましょう。
5)プログラムを書く───────────────────────────5)
-
早速、プログラム全体を書いてみてください。インクルード文、変数、関数の宣言等を忘れないようにして下さい。
:
:
:
:
:
:
:
:
:
では解答です。
演習プログラム3−1
/* Program */
/* Short Program 5 */
/* 最大公約数を求める 1 */
#include <stdio.h>
int gcd(int a, int b);
main()
{
int x, y, z;
scanf("%d", &x);
scanf("%d", &y);
z = gcd(x, y);
printf("%d\n", z);
}
int gcd(int a, int b)
{
int c;
while (b <> 0)
{
c = a % b;
a = b;
b = c;
}
return a;
}
|
どうでしょうか。こんな感じになりましたか??
では、解説しましょう。
まず、今回も特別な標準関数は用いていないので、インクルードするのはstdio.hだけでいいでしょう。
次に、最大公約数を計算する関数を宣言しています。返り値の型はint型、関数名はgcd、引数は二つでともint型です。
メインルーチンは、変数を3つ宣言しており、それぞれ、2つの数を入力する変数と、その最大公約数を入れる変数になります。
それから二つの値を入力し、その後それを関数に引数として渡し、最大公約数を求め、その結果を出力して終りです。
問題の関数の方ですが、a、bは引数として渡されるので、余りcを宣言するだけです。後は、先のアルゴリズムに従って、書くだけです。
最後に答えのaをリターン文で返します。
6)コンパイル──────────────────────────────6)
-
いつものお約束のコンパイルです。くどいようですが、間違いが多いのは、
・”;”がない。
・”{}”の対応があってない。
など結構、初歩的なミスです。
最近のコンパイラはかなり親切にエラーメッセージを出してくれるので、それほど苦労することもないでしょうが、たまに分からなくなることがあります。そういう時は大抵これだと思って間違いないでしょう。エラーがでたらもう一度エディタで書き直します。
リンクが必要なコンパイラは、リンクもしておきましょう。
7)テスト────────────────────────────────7)
-
完成したらテストしてみてください。適当に2つの整数を入れてみて、プログラムがきちんと動くかどうか見てください。動いたら完成です。
8)改良─────────────────────────────────8)
-
今回の改良点は以下の通りです。
・入力をscanf()ではないものにする。
・入力がx<yでもきちんと動くようにする。
・最大公約数を再帰的に求めるようにする。
・大きな整数も扱えるようにする。(100,000とか10,000,000,000位)
最初の点については、ここではやりません。いずれ機会があるでしょうから、その時にgetchar()を用いた方法について述べます。
2点目は、scanf()で二つの数を読み込んだ後、大小を比較して、大きい方がx小さい方がyになるようにすればいいでしょう。
3点目は、後半で解説します。
4点目は、根本的にプログラムを改造する必要があります。
コンピュータでは通常大きな数(または小さな数)を扱う時は、配列を用いて計算します。例えば大きさ100の整数型の配列を用意し、各要素として各桁の数値(0〜9)をいれるとするとこの配列で100桁の数値が表現できることになります。これに対して、四則演算を行うサブルーチンを書き、比較するサブルーチンを書かなければならないわけです。出来ないことはありませんが、今はまだ難しいでしょう。(別にやってみてもかまいませんが)
9)完成─────────────────────────────────9)
-
と言うわけで、非再帰版はこれで完成です。
一休みしたら、再帰版に進みましょう。