ホームページへ戻る
第2章(1): 多重ループ探索先ほどの虫食い算に習って、今度はこの問題を解いてみましょう。
基本方針として、[1]のカードからペアで順番に配置していき、また、左側への可能な配置から順次試していくことにします。 さて、前回は未知数が2つだったのでfor文を二重に重ねましたが、今回は7種類のカードの位置が未知数なのでfor文を7重に重ねることになります。List.2-1 このように未知数の数だけfor文を重ねれば全パターンを調べることが出来ます。もし未知数が20個あればfor文を20個重ねることになります。 しかし、これは大変です。もっといい方法は無いものでしょうか。ほとんどのプログラム言語には「再帰呼び出し」という便利な機能があって、for文をたくさん重ねなくとも、もっと簡潔にプログラムを書く方法があるのです。 第2章(2): 再帰呼び出しあなたがもし「折半」という言葉の意味を知りたくて国語辞典を調べたとします。すると、こう書いてありました。
こんな国語辞典はありません。「折半」を説明するのに「折半」という言葉を使うのはおかしい。でも、プログラミングの世界では、このような記述を可能にしているのです。 nの階乗値を返す関数 List.2-2 をご覧下さい。関数の中で自分自身を呼び出しています。 そして、4の階乗なら、「4」×「3の階乗」を答えとして返します。一見すると不思議に思えますが、呼び出すときのnの値が異なります。つまり、全く同じものを呼び出しているのでは無いのです。 自分自身をそのまま呼び出すのではなく、自分の複製を作ってそれを呼び出すと考えると分かり易いかも知れません。List.2-3 パズル問題をプログラムで解くとき、この「再帰呼び出し」が大活躍します。では、例題3に戻りましょう。 この再帰呼び出しを使って例題3を解くプログラムは List.2-4 のように簡潔になります。for文は1個しかありませんが、処理の流れを追いかければfor文は7重に実行されます。 再帰呼び出しを使う上で、大切なポイントが2つあります。
ホームページへ戻る |
List.2-1 7重のforループを使う // 1のカードを配置 for (L1=0,R1=2; R1<14; L1++,R1++) { TABLE[L1] = TABLE[R1] = 1; // 置く // 2のカードを配置 for (L2=0,R2=3; R2<14; L2++,R2++) { if (TABLE[L2]==0 && TABLE[R2]==0) { // 置けるなら TABLE[L2] = TABLE[R2] = 2; // 置く // 3のカードを配置 for (L3=0,R3=4; R3<14; L3++,R3++) { if (TABLE[L3]==0 && TABLE[R3]==0) { TABLE[L3] = TABLE[R3] = 3; // 4のカードを配置 for (L4=0,R4=5; R4<14; L4++,R4++) { ---(省略)--- // 7のカードを配置 for (L7=0,R7=8; R7<14; L7++,R7++) { if (TABLE[L7]==0 && TABLE[R7]==0) { TABLE[L7] = TABLE[R7] = 7; // TABLE[]を表示する:省略 TABLE[L7] = TABLE[R7] = 0; // 消す } } ---(省略)--- } TABLE[L3] = TABLE[R3] = 0; // 消す } } TABLE[L2] = TABLE[R2] = 0; // 消す } } TABLE[L1] = TABLE[R1] = 0; // 消す } List.2-2 nの階乗を返す関数 int Kaijyo(int n) { if (n < 2) { return 1; } else { return n * Kaijyo(n-1); // 自分を呼び出している } } List.2-3 4の階乗が求まる実際の流れ int Kaijyo1() // nを1に変えて複製された関数 { return 1; } int Kaijyo2() // nを2に変えて複製された関数 { return 2 * Kaijyo1(); } int Kaijyo3() // nを3に変えて複製された関数 { return 3 * Kaijyo2(); } int Kaijyo4() // n=4の階乗を求める関数 { return 4 * Kaijyo3(); } List.2-4 再帰呼び出しで例題3を解く int TABLE[14]; // カードを置いていくテーブル void Card(int n) { int L, R; if (n > 7) { // すべてのカードが置かれた // TABLE[]を表示する:省略 } else { // nのカードを配置 for (L=0,R=n+1; R<14; L++,R++) { if (TABLE[L]==0 && TABLE[R]==0) { // 置けるなら TABLE[L] = TABLE[R] = n; // 置く Card(n+1); // 再帰呼び出し TABLE[L] = TABLE[R] = 0; // 消す } } } } void Puzzle() { Card(1); // 最初に1のカードを置く } |