第9講 関数の再帰的使用
第4話 for文版順列作成の解説その1
お約束の通り、3次順列のおけるfor文版の解説を行います。
かなり難解だからです。
#pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
int i,j,k,l,h1,h2;
int *a;
long s=0;
dataGridView1->Rows->Clear();
a=(int* )malloc(12);
String^ w=L"";
for(i=1;i<4;i++){
a[0]=i;
for(j=1;j<4;j++){
a[1]=j;
h1=1;
if(a[0]==a[1])h1=0;
if(h1==1){
for(k=1;k<4;k++){
a[2]=k;
h2=1;
for(l=0;l<2;l++){
if(a[l]==a[2]){
h2=0;
break;
}
}
if(h2==1){
dataGridView1->Rows->Add();
for(l=0;l<3;l++){
dataGridView1[l,s]->Value =a[l];
}
s++;
}
}
}
}
}
dataGridView1->Rows->Add();
dataGridView1[12,s+1]->Value =s.ToString();
}
};
}
プログラムを読むときに、絶対欠かすことのできないことは、各変数の役割がなんなのかを理解することです。
各変数の枠割りを理解しないと、プログラミングコードは何度読んでもさっぱり意味のわからないものになります。
4次元ループになっているので、for文を制御する変数はi、j、k、lの4つです。
3次配列なのになぜ4次元なのでしょうか。
ここの理解が、次の自己再帰型を理解する際の要になります。
さて、i、j、kの役割は比較的はっきりしています。つまり、最初の3つの次元は比較的に理解が容易です。
0 | 1 | 2 |
最初の3つの次元は、セルの3に対応しています。
言い換えれば、3次配列の3に対応しています。
つまり、i、j、kはそれぞれセルに入る数字1,2,3に対応します。
各セルには1,2,3の数字が入ります。
1 | 2 | 3 |
1 | 3 | 2 |
2 | 1 | 3 |
2 | 3 | 1 |
3 | 1 | 2 |
3 | 2 | 1 |
つまり、色を対応させると
i、j、k |
となります。iは一番目のセルに入る数字1,2,3
jは2番目のセルに入る数字1,2,3
kは3番目のセルに入る数字1,2,3に対応しています。
では、lの役割はなんでしょうか。
実は、自己再帰型ではこれが枢要な役割を果たします。
(ただし、次話で示すコーティング例ではlはgの名称になっています。)
0 | 1 | 2 |
実は、lはセルの位置を示しています。
つまりセル番号を意味しています。
プログラミングを理解するときに、セル番号とセルに入る数字を明確に区別しなければなりません。
変数を理解するときに箱のラベルと箱の内容を区別しなければならなかったのと同じです。
ビール瓶でたとえれば、ラベルは『一番搾り』で内容は『ビール』です。
セル番号lは、箱のラベルに相当します。
入っている数字i、j、kは、箱の内容の相当します。
具体的には
では役割を明確にしたところで、トレースしてみましょう。
以下は、色を対応させながら見てください。
i=1のとき、
a[0]=i;から0番目のセルに内容1が入ります。
セルの内容 | 1 | 2 | 3 |
セル番号 | 0 | 1 | 2 |
そして、
for(j=1;j<4;j++){
2次元目のループすなわちセル番号1の世界に入り、
a[1]=j;から
セルの内容 | 1 | 1 | 3 |
セル番号 | 0 | 1 | 2 |
セル番号1のセルに内容1が入ります。そして、
h1=1;
if(a[0]==a[1])h1=0;
の2行によって、h1=0となります。a[0]もa[1]も内容は1、1であるからです。
すると、
if(h1==1){
for(k=1;k<4;k++){
a[2]=k;
h2=1;
for(l=0;l<2;l++){
if(a[l]==a[2]){
h2=0;
break;
}
}
if(h2==1){
dataGridView1->Rows->Add();
for(l=0;l<3;l++){
dataGridView1[l,s]->Value =a[l];
}
s++;
}
}
}
の部分は実行されません。そして、2次元(jのループ)の2巡目に入り、a[1]=j;から
セルの内容 | 1 | 2 | 3 |
セル番号 | 0 | 1 | 2 |
となります。そして、今回は
h1=1;
if(a[0]==a[1])h1=0;
a[0]の内容は1、a[1]の内容は2で異なっていますので、
if文は実行されず、h1は書き換えられず1のままですから、
if(h1==1){
for(k=1;k<4;k++){
a[2]=k;
h2=1;
for(l=0;l<2;l++){
if(a[l]==a[2]){
h2=0;
break;
}
}
if(h2==1){
dataGridView1->Rows->Add();
for(l=0;l<3;l++){
dataGridView1[l,s]->Value =a[l];
}
s++;
}
}
}
が実行されます。
for(k=1;k<4;k++){
で3次元目のループ=セル番号2の世界=kのループ に入ります。
a[2]=k;によって、
セルの内容 | 1 | 2 | 1 |
セル番号 | 0 | 1 | 2 |
となります。
for(l=0;l<2;l++){
if(a[l]==a[2]){
h2=0;
break;
}
}
の1巡目のループにおいて、1と1が比較され内容が同じなので、
if(a[l]==a[2]){
h2=0;
break;
}
によって、h2は0となり、for文も強制的に終了となります。
3次元目の2回目のループのa[2]=k;によって、
セルの内容 | 1 | 2 | 2 |
セル番号 | 0 | 1 | 2 |
となります。そして、
for(l=0;l<2;l++){
if(a[l]==a[2]){
h2=0;
break;
}
}
の1巡目のループによって、1と2が比較されますが、今度は内容が違うのでif文はスルーされます。
ところが、2巡目のループによって2と2が比較され内容が同じなので、if文が実行されてしまいh2は0となってしまいます。すると、
if(h2==1){
dataGridView1->Rows->Add();
for(l=0;l<3;l++){
dataGridView1[l,s]->Value =a[l];
}
s++;
}
は実行されず、3次元目ループの3巡目に入り、a[2]=k;によって、
セルの内容 | 1 | 2 | 3 |
セル番号 | 0 | 1 | 2 |
となります。3巡目においては、
for(l=0;l<2;l++){
if(a[l]==a[2]){
h2=0;
break;
}
}
は1回目において1と3が、2回目において2と3が比較されますが、いずれも内容が違うのでif文は一度も実行されずに終わりますので、
h2は1のままで、
if(h2==1){
dataGridView1->Rows->Add();
for(l=0;l<3;l++){
dataGridView1[l,s]->Value =a[l];
}
s++;
}
が実行され、1個目の順列がデータグリッドビューに表示され、s++;によって、順列の個数が1とカウントされます。
変数sの役割は、順列総数のカウントだったわけです。
かなり、長くなったので以降のトレースは次話で。
第3話へ 第5話へ