第15講 関数の再帰的呼び出し
第2話 関数の再帰的利用のメリット
3つのすべての順列
を作るプログラムの解説の前に、
この講の主題である関数の再帰的呼び出しによる方法だと、
どのようなソースになるかお見せしましょう。
#pragma once
int n=3 ;
int a[3];
int s;
・
・
・
#pragma endregion
String^ w;
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
w=L"";
s=0;
f(0);
w+=L"\n"+L"順列総数:"+s.ToString();
label1->Text=w;
}
void f(char g){
int i,j,k,h;
for(i=0;i<n;i++){
a[g]=i+1;
h=1;
if(g>0){
for(j=0;j<g;j++){
if(a[g]==a[j]){
h=0;
break;
}
}
}
if(h==1){
if(g<n-1){
f(g+1);
}
else{
for(k=0;k<n;k++)w+=a[k].ToString()+L"
";
w+=L"\n";
s++;
}
}
}
}
関数の再帰的呼び出しといわれる所以は、f(g+1); にあります。
関数の中で自分自身を呼び出しています。
このプログラムソースは、初心者の方にとってはもちろんのこと、
ある程度プログラムの勉強をされた方にとっても、
理解できない,というところが正直なところではないでしょうか。
むしろ、第1話のfor文のソースより難解であるといってよいと思います。
ですから、このソースの解説については、
3話か4話に渡ってかなり時間をかけて解説していきます。
ですが、ここでの注目点だけ説明しておきましょう。
このプログラムは、1,2,3の3つの順列を作成するようになっていますが、
int n=3 ;の3 を例えば7に変更するだけで、
1,2,3,4,5,6,7の7つの順列を作成するように変更することができます。
さらに、Form1に次のようにテキストボックスを入れておいて、
そこから、nの値を取得するようにしておけば、
何個の順列でも作成可能な普遍的(広範囲)なプログラムに変身します。
#pragma once
int n;
int a[7];
int s=0;
・
・
・
#pragma endregion
String^ w1;
String^ w2;
String^ w3;
String^ w4;
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
w1=L""; w2=L""; w3=L"";
w4=L"";
n=int::Parse(textBox1->Text);
f(0);
w4+=L"\n"+L"順列総数:"+s.ToString();
label1->Text=w1;
label3->Text=w2;
label4->Text=w3;
label5->Text=w4;
}
void f(char g){
int i,j,k,h;
for(i=0;i<n;i++){
a[g]=i+1;
h=1;
if(g>0){
for(j=0;j<g;j++){
if(a[g]==a[j]){
h=0;
break;
}
}
}
if(h==1){
if(g<n-1){
f(g+1);
}
else{
if(s<2000){
for(k=0;k<n;k++)w1+=a[k].ToString()+L"
";
w1+=L"\n";
}
if(s<4000 && s>=2000){
for(k=0;k<n;k++)w2+=a[k].ToString()+L"
";
w2+=L"\n";
}
if(s<6000 && s>=4000){
for(k=0;k<n;k++)w3+=a[k].ToString()+L" ";
w3+=L"\n";
}
if(s<8000 && s>=6000){
for(k=0;k<n;k++)w4+=a[k].ToString()+L"
";
w4+=L"\n";
}
s++;
}
}
}
}
labelを増やした理由は、labelで表示できる行数に限りがあるからです。
実験では3000行は表示できないようなので、各labelで2000行表示させるようにしてあります。
尚、Form1のプロパティのAutoScrollはTrueにしておいて下さい。
スクロールできるようにしておかないとすべての表示ができないからです。
2000行ずつで4つのlabelを用意しましたが、
これでも7個の順列(総数は7!=5040個)が限界です。
この問題を解決するため後にlabelではなく、DataGridViewを利用する方法を紹介します。
DataGridViewなら何万行でも表示可能です。
それに対して、for文の方はソースを書く気力が失せるほど大変です。
今回は、Visual C++で書く気力が起きませんので、
VB講義から同様なプログラムソースを引用させて頂きます。
以下のソースは、初心者の方(プログラミングの経験が全くない方を想定しています。)にとっては、見たことがないVisual Basicによるソースです。
ですから、大変さだけ感じ取って頂ければよいと思います。
ですが、言語が違っても雰囲気は似ているのはお分かりになると思います。
Visual C++をここまで勉強されてきた皆さんは、もちろん、なんとなくですがお分かりになるのではないでしょうか。 Private Sub CommandButton1_Click()
Dim i As Integer, j As Integer, k As
Integer, l As Integer, h1 As Integer, h2 As Integer, h3 As Integer, h4 As
Integer, h5 As Integer, h6 As Integer, jyn(7) As Integer Dim sousuu As
Long
sousuu = 0 For i = 1 To 7 jyn(1) = i For j = 1
To 7 jyn(2) = j h1 = 1 For k = 1 To 1 If
jyn(2) = jyn(k) Then h1 = 0 Exit For End
If Next If h1 = 1 Then For k = 1 To
7 jyn(3) = k h2 = 1 For l = 1 To
2 If jyn(3) = jyn(l) Then h2 =
0 Exit For End
If Next If h2 = 1 Then For l = 1 To
7 jyn(4) = l h3 = 1 For m = 1
To 3 If jyn(4) = jyn(m) Then h3 =
0 Exit For End
If Next If h3 = 1
Then For m = 1 To 7 jyn(5) =
m h4 = 1 For n = 1 To
4 If jyn(5) = jyn(n) Then h4
= 0 Exit For End
If Next If h4 = 1
Then For n = 1 To 7 jyn(6) =
n h5 = 1 For o = 1 To
5 If jyn(6) = jyn(o)
Then h5 = 0 Exit
For End
If Next If h5 = 1
Then For o = 1 To
7 jyn(7) = o h6 =
1 For p = 1 To 6 If
jyn(7) = jyn(p) Then h6 =
0 Exit
For End
If Next If h6 = 1
Then
sousuua = sousuu Mod
8 sousuus = Int(sousuu /
8) sousuu = sousuu +
1
For p = 1 To
7 Cells(5 + sousuus * 2, 1 + p + sousuua * 8) =
jyn(p) Next End
If Next End
If Next End
If Next End
If Next End If Next End
If Next Next
End Sub
7個の順列ですでにこんなに大変です。
これが10個、20個になるととても手に負えません。
それに対して、関数の再帰的呼び出しの方は、
表示の限界の問題さえ解決すれば、3次の順列のプログラムを少し改良するだけで、
普遍的なプログラムに変身します。
for文の場合、7次元ループぐらいが限界ですが、
関数の再帰的呼び出しの方は、理論的には無限次元のループが可能になります。
後で、何回も予告しておいた通り、数独問題作成ソフトの開発に挑戦しますが、
数独問題作成ソフトにおいては、
81×4=328次元ループになっています。
さらに、魔方陣作成にも挑戦しますが、200次魔方陣当たりでは20万次ループを軽く越えます。
このような多次元ループを実現しているものこそが、関数の再帰的呼び出しなのです。
第11講第6話へ 第12講第1話へ 第14講第10話へ 第15講第1話へ 第15講第3話へ
VC++講義第1部へ
vb講義へ
VB講義基礎へ
初心者のための世界で一番わかりやすいVisual C++入門基礎講座
初心者のための世界で一番わかりやすいVisual
Basic入門基礎講座