第9講 関数の再帰的使用
第5話 関数の再帰的使用によるn次順列生成解説その2

コード再掲
#include<iostream>
using namespace std;
void f(int g);
void h();
int x[15],n;
long cn;
void main(){
   cout<<"何次順列を生成しますか?"<<endl;
   scanf("%d",&n);
   cout<<endl;
   cn=0;
   f(0);
   cout<<n<<"次順列が"<<cn<<"個生成されました。"<<endl;
}
void f(int g){
   int i,j;
   for(
i=1;i<n+1;i++){
     x[g]=
i;
     if(
g>0){
        for(j=0;j<g;j++){
          if(x[g]==x[j])goto tobi;
        }
     }
     if(
g+1<n){
        f(
g+1);
     }
     else{
        h();
        cn++;
     }
     tobi:;
   }
}
void h(){
   for(char i=0;i<n;i++)cout<<x[i]<<" ";
   cout<<endl;
}

解説
最初
int x[15],n;
long cn;

はグローバルにしないで、
f(g,n,cn,x) として関数を呼び出していましが、
次元を決定づけているものはgであることを強調するために、
今回はx[15]とn、cnはグローバル配列とグローバル変数にしてあります。
このためにf(g)というすっきした呼び出しになっています。
尚、f(g,cn,n,x) とする場合は
**xの最初のアドレスを引数として引き渡していたのでしたね。
**xは実質2次元配列と同じものでしたから、
実質2次元配列を渡していたのです。
   cout<<"何次順列を生成しますか?"<<endl;
   scanf("%d",&n);
   cout<<endl;
   cn=0;

2行目で、コンソールから順列の次数を取得しています。
4行目で初期化しています。
cnは、順列総数を数えるカウンタ
(数えるものという意味です)変数です。
さて、トレースを今からしていきますが、
現在どのセル番号の世界にいるのかを常に明確に意識しながら、
以下をお読みください。
現在どのセルにいるのかをはっきり掴んでいないと、
トレースが理解できなくなります。
現在いるセル(現在アクティブなセル)を

   

で表すことにします。



また、n=3の場合b
でトレースしていることも念頭においてください。
  f (0)
ではじめて、世界

 0

が生成します。
というのは、関数は呼び出されたときのみ、
コンピュータのメモリ上に生じるからです。
   for(i=1;i<n+1;i++){
     x[g]=
i;
最初にiに1が入ります。

 0
1

     if(g>0){
        for(j=0;j<g;j++){
          if(x[g]==x[j])goto tobi;
        }
     }

g = 0ですから、If文は実行されませんから
tobiへの跳躍は行われず、
     if(g+1<n){
        f(
g+1);
     }
     else{
        h();
        cn++;
     }

実行されます。
g = 0ですから、
g + 1
< nは 0+ 1 < nすなわち1 < 3 で成り立ちますので、
        f(
g+1);
の命令が遂行されます。
g = 0
ですので、f (
g + 1)はf (1)です。
したがって、f (
0)がf (1)を呼び出しました。
その結果、2番目の世界(セル番号
の世界)

 0
1

が発生します。
   for(i=1;i<n+1;i++){
     x[g]=
i;
によって、

 0
1 1

となります。g = 1ですから、
   for(i=1;i<n+1;i++){
     x[g]=
i;
     if(
g>0){
        for(j=0;j<g;j++){
          if(x[g]==x[j])goto tobi;
        }
     }
のIf文が実施され、j = 0のとき、
x[
g] = x[j] は x[1] = x[0] となるためにgoto tobi;
(セル番号の世界にいるからですよ。)
が行われために

     if(g+1<n){
        f(g+1);
     }
     else{
        h();
        cn++;
     }
は遂行されません。したがって、for文は次ぎに進みi=2となり、

 0
1 2

今度はj=0のとき、x[1]≠x[0]なので、goto tobi;は行われず、
     if(g+1<n){
        f(g+1);
     }
     else{
        h();
        cn++;
     }
が実行されます。
g = 1ですから、g + 1 < n は 1 + 1 < 3で真ですから、
        f (
g + 1)すなわちf (2)
の命令が遂行されます。すなわち、f (
1)がf (2)を呼び出し、

 0
1 2

3番目の世界(g=2の世界)が誕生します。
     if(g+1<n){
        f(g+1);
     }
     else{
        h();
        cn++;
     }
によって、i=1が入り、

 0
1 2 1

となります。
     if(g>0){
        for(j=0;j<g;j++){
          if(x[g]==x[j])goto tobi;
        }
     }
は1回目のループすなわちj = 0のとき、
x[
g] = x[j] は x[2] = x[0]となりますからgoto tobi;によって、
     if(g+1<n){
        f(g+1);
     }
     else{
        h();
        cn++;
     }
は遂行されず、for文は進みi=2となり、

 0
1 2 2

となります。
     if(g>0){
        for(j=0;j<g;j++){
          if(x[g]==x[j])goto tobi;
        }
     }
今回は、2回目のループすなわちj=1のとき、x[g] = x[j] は x[2] = x[1]となり、
     if(g+1<n){
        f(g+1);
     }
     else{
        h();
        cn++;
     }
は実施にいたらず、for文は最後に進み、

 0
1 2 3

となります。x[2]≠x[0]、x[2]≠x[1]なので、はじめて
     if(g>0){
        for(j=0;j<g;j++){
          if(x[g]==x[j])goto tobi;
        }
     }
のチェックをクリアして、
     if(g+1<n){
        f(g+1);
     }
     else{
        h();
        cn++;
     }
が実行されるわけですが、g = 2 ですから
g + 1 < n は 2 + 1 < 3で成り立ちませんので、
elseの部分が実行され
f (2)ははじめて自分だけに
与えられている核ボタンを押す権限=表示関数h()呼び出す権限
     else{
        h();
        cn++;
     }
を遂行して、


シートに1個目の順列aを表示させることに成功します。

本日のトレースはここまで。これ以降のトレースは次話で。





第4話へ 第6話へ


a

魔方陣 数独で学ぶ VBA 入門
数独のシンプルな解き方・簡単な解法の研究
VB講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座
初心者のための世界で一番わかりやすいVisual Basic入門基礎講座
初心者のための世界で一番わかりやすいVBA入門講義(基礎から応用まで)
初心者のための VC++による C言語 C++ 入門 基礎から応用まで第1部
eclipse java 入門
java 入門 サイト 基礎から応用まで
VC++ C言語 C++ 入門 初心者 基礎から応用まで
本サイトトップへ