マルチスレッド版数独自動生成ソフトC++コードを題材とする超初心者のためのVisual Studio C++講義
第9章 関数の再帰的使用

第12話 部屋数3・生徒数3の場合におけるトレースその4

プログラムコード再掲

#include<iostream>//インクルードファイルiostreamの読み込み

#include<conio.h>//while(!_kbhit());を使うためのお呪い

#include<strins> //文字列変数を使えるようにするために組み込む

#include <iomanip> //setprecisionを使えるように組み込む

#include <cmath>//powなどを使うときに必要

#include <ctime>//time()(←現時刻発生する関数)を使うために必要

using namespace std;//coutを使うときに必要なお呪い

const int n = 5;//具体的な数字を使うのではなく、 n を使うと汎用性のあるプログラムになる!

void f(int s);//順列生成関数←引数名をgからsに変更(2026年3月19日)

int a[25]; //将来5次魔方陣まで生成できるように25に変更

int cn = 0; //anに変更 変更理由はnuberの頭文字nは整数を表す場合が多いからです

int main() {

              f(0);

              cout << endl << "生成された順列は" << cn << "個です。" << endl;//←2026年3月19日訂正

              while (!_kbhit());//待機させるための命令

              return(0);

}

void f(int s) {

              int h;

              for (int i = 0; i < n; i++) {

                            if (s == 0)a[s] = i + 1;

                            h = 1;

                            if (s > 0) {

                                          for (int j = 0; j < s; j++) {

                                                        if (a[j] == i + 1) {

                                                                      h = 0;

                                                                      break;

                                                        }

                                          }

                                          if (h == 1)a[s] = i + 1;

                            }

                            if (h == 1) {

                                          if (s + 1 < n) {

                                                        f(s + 1);

                                          }

                                          else {

                                                        for (int j = 0; j < n; j++)cout << a[j];

                                                        cout << " ";

                                                        cn++;

                                                        if (cn % 10 == 0)cout << endl;

                                          }

                            }

              }

}




←2個目

      から2番目の順列が生成され

                                         else {

                                                        for (int j = 0; j < n; j++)cout << a[j];

                                                        cout << " ";

                                                        cn++;

                                                        if (cn % 10 == 0)cout << endl;

                                          }

      によってコンソール画面にが打ち出されます。

      さて、旅を再開しましょう。

void f(int s) {

              int h;

              for (int i = 0; i < n; i++) {

                            if (s == 0)a[s] = i + 1;

                            h = 1;

                            if (s > 0) {

                                          for (int j = 0; j < s; j++) {

                                                        if (a[j] == i + 1) {

                                                                      h = 0;

                                                                      break;

                                                        }

                                          }

                                          if (h == 1)a[s] = i + 1;

                            }
      for文となり、i = 2 となります。


      すると i + 1 = 3 ですから重複検査をクリアできずに

                                          if (h == 1)a[s] = i + 1;

      は実行さえれずに、for文最後となりi = 2 となりますから、

       i + 1 = 3 となり、

       重複検査を通らず、

                                         if (h == 1)a[s] = i + 1;

      は実行されません。

      すると、for文は最後まで行きましたから、

      f(2)は再び消滅して

      となります。i = 2でしたから、for文は終わり今度はf(1)が消滅して

      となっています。

void f(int s) {

              int h;

              for (int i = 0; i < n; i++) {

                            if (s == 0)a[s] = i + 1;

                            h = 1;

                            if (s > 0) {

                                          for (int j = 0; j < s; j++) {

                                                        if (a[j] == i + 1) {

                                                                      h = 0;

                                                                      break;

                                                        }

                                          }

                                          if (h == 1)a[s] = i + 1;

                            }
      for文2巡目となり、i = 1 となります。

      s = 0 ですから

                            if (s == 0)a[s] = i + 1;

      は実行されて

                   a[0] = 2

      となり、

      になります。

                           if (h == 1) {

                                          if (s + 1 < n) {

                                                        f(s + 1);

                                          }
      s = 0 より s + 1 = 1 で


                                                        f(s + 1);

     は実行され、f(0)がf(1)を呼び出し、f(0)の中に人形f(1)が生成され

     となります。


void f(int s) {

              int h;

              for (int i = 0; i < n; i++) {

                            if (s == 0)a[s] = i + 1;

                            h = 1;

                            if (s > 0) {

                                          for (int j = 0; j < s; j++) {

                                                        if (a[j] == i + 1) {

                                                                      h = 0;

                                                                      break;

                                                        }

                                          }

                                          if (h == 1)a[s] = i + 1;

                            }
      for文の1巡目となり、i = 1 となります。
 
     これは

                            if (s > 0) {

                                          for (int j = 0; j < s; j++) {

                                                        if (a[j] == i + 1) {

                                                                      h = 0;

                                                                      break;

                                                        }

                                          }

                                          if (h == 1)a[s] = i + 1;

                            }
      a[j] = 2 ですか重複検査をクリアして

                           if (h == 1) {

                                          if (s + 1 < n) {

                                                        f(s + 1);

                                          }
        
       f(s + 1)すなわちf(2)が実行されて

       人形f(1)の中に人形f(2)が生成されて上図のようになります。

              for (int i = 0; i < n; i++) {

                            if (s == 0)a[s] = i + 1;

                            h = 1;

                            if (s > 0) {

                                          for (int j = 0; j < s; j++) {

                                                        if (a[j] == i + 1) {

                                                                      h = 0;

                                                                      break;

                                                        }

                                          }

                                          if (h == 1)a[s] = i + 1;

                            }
      for文の1巡目となり、i = 0 となり i + 1 = 1 となり、

      これは重複検査にパスせずに

      for文の2巡目となり、i = 1 となり i + 1 = 2 となり、

                                         for (int j = 0; j < s; j++) {

                                                        if (a[j] == i + 1) {

                                                                      h = 0;

                                                                      break;

                                                        }

                                          }
      

      再び重複検査にはじかれ

      for文の3巡目となり、i = 2 となり i + 1 = 3 となり、

      初めて重複検査を通り、

                                          if (h == 1)a[s] = i + 1;

      が実行されてa[2] = 3 となり、
←3個目
      3番目の順列の生成に成功して

                                         else {

                                                        for (int j = 0; j < n; j++)cout << a[j];

                                                        cout << " ";

                                                        cn++;

                                                        if (cn % 10 == 0)cout << endl;

                                          }

      によってコンソール画面にが打ち出されます。

      f(2)のfor文は終わっていてf(3)の任務が終わり運命を閉じます。

      f(2)の2巡目を迎え

      これは重複検査に抵触して3巡目になり、

      となります。以降は細かい動きは省いて結果だけ示します。
←4個目










←5個目









←6個目

まだ、終わっていませんが一応6個目ができたことに満足してトレースを終了します。

書いている私が混乱しましたから、

呼んでいる皆さんは大混乱であったかもしれませんが、

1つ1つ丁寧にトレースしてください。

入れ子式人形を思いついたのはヘーゲルからの連想と話したことは、

間違いありませんが、

もっと、直接的な理由を思い出しました。

5次元for文のときなどが入れ子式になっていることが、

入れ子式人形を採用するようになった理由です。

再帰を入れ子式人形を使って説明している例は、

見たことがありませんので、おそらく私の独創と思われます。

さて、トレースは終わりにしまして、次話の課題です。

それは、関数の再帰的利用による1 + 2 + 3 + ・・・ + n の計算を考えてください。


1から10までの和は55です。




第9章第11話へ 第9章第13話へ

本講義トップへ