最終講 卒業研究と卒業試験
第2話 課題1 順列を2次元に並べる
課題1は前に作った順列作成ソフトを少し改良すれば出来ます。
9次順列を2次元に並べるだけです。
セルの内容 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
セル番号 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
1 | 2 | 3 |
0 | 1 | 2 |
4 | 5 | 6 |
3 | 4 | 5 |
7 | 8 | 9 |
6 | 7 | 8 |
下のコーティングのgはセル番号に対応しています。
順列のときと同じでセルのラベル(すなわちセル番号)であるgとセルに入っている数字(セルの内容)を区別しないと訳がわからなくなります。
ビール瓶で例えれば、『一番搾り』がラベルで、内容が『ビール』でしたね。
セル番号(セルのラベル)と内容(1から9までの行列を構成する数字)を明確に区別して下さい。
上図の右側のiはfor(i=0;i<9;i++)のiに対応し、gはvoid f(char g)のgです。
このgは0から8へと進んでいきます。
1 | 2 | 3 |
0 | 1 | 2 |
4 | 5 | 6 |
3 | 4 | 5 |
7 | 8 | 9 |
6 | 7 | 8 |
次のコーティング例は、この課題だけとしては少し難しいかもしれませんが、
最終的には数独(ナンバープレイス)解答作成が目的ですから、
それとの関連を考えてのことです。
コーティング例
#pragma once
int a[3][3];
int x[9],y[9];
int s;
namespace 順列方陣 {
using namespace System;
・
・
・
#pragma endregion
private: System::Void button1_Click(System::Object^ sender, System::EventArgs^
e) {
s=0;
h();
f(0);
dataGridView1->Rows->Add();
dataGridView1[8,4*s]->Value=L"順";
dataGridView1[9,4*s]->Value=L"列";
dataGridView1[10,4*s]->Value=L"方";
dataGridView1[11,4*s]->Value=L"陣";
dataGridView1[12,4*s]->Value=L"数";
dataGridView1[13,4*s]->Value=s.ToString();
}
void h(){
char i;
for(i=0;i<9;i++){
x[i]=i%3;
y[i]=i/3;
}
}
void f(char g){
if(s==100)return; 'すべて計算させるとかなり時間がかかるので100個で止めている。
int i,j,k,h;
for(i=0;i<9;i++){
a[y[g]][x[g]]=i+1;
h=1;
if(g>0){
for(j=0;j<g;j++){
if(a[y[g]][x[g]]==a[y[j]][x[j]]){
h=0;
break;
}
}
}
if(h==1){
if(g+1<9){
f(g+1);
}
else{
for(j=0;j<3;j++){
dataGridView1->Rows->Add();
for(k=0;k<3;k++){
dataGridView1[k,j+4*s]->Value=a[j][k];
}
}
dataGridView1->Rows->Add();
s++;
}
}
}
}
};
}
実行例
解説
dataGridView1->Rows->Add();
dataGridView1[8,4*s]->Value=L"順";
dataGridView1[9,4*s]->Value=L"列";
dataGridView1[10,4*s]->Value=L"方";
dataGridView1[11,4*s]->Value=L"陣";
dataGridView1[12,4*s]->Value=L"数";
dataGridView1[13,4*s]->Value=s.ToString();
ここは、次のようにしても同じ結果が得られます。
array<String^>^ w=gcnew array<String^>(14);
w[8]=L"順";w[9]=L"列";w[10]=L"総";w[11]=L"数";w[12]=L":";w[13]=s.ToString();
dataGridView1->Rows->Add(w);
array<String^>^ w=gcnew array<String^>(14);はString^型で要素数14の配列を宣言しています。
配列の宣言は
int a[10];
のようにしてきましたが、
array<int>^ a=gcnew array<int>(10);
でも宣言できます。
一般にarray<変数型>^ 変数名=gcnew array<変数型>(要素数);
で配列を宣言することが出来ます。
gcnewは新しく定義するという意味です。
array<String^>^ w=gcnew array<String^>(14);でString^型を収納する配列を用意すると、
その配列の添え字順にdataGridView1のセルに順にデータが入っていきます。
void g(){
char i;
for(i=0;i<9;i++){
x[i]=i%3;
y[i]=i/3;
}
}
では座標を計算しています。1次元のデータを2次元に対応させるためのものです。
x[0]=0,x[1]=1,x[2]=2
x[3]=0,x[4]=1,x{5]=2
x[6]=0,x[7]=1,x{8]=2
y[0]=0,y[1]=0,y[2]=0
y[3]=1,y[4]=1,y{5]=1
y[6]=2,y[7]=2,y{8]=3
と対応します。これを使えば1次元データを2次元に配置することが出来るのです。
尚、色対応でお分かりかと思いますが、ここでのiは、void f(char g)のgに対応します。
gはセル番号=セルのラベルです。
今回のプログラミンで眼目部分は、void f(char g)です。
これが自己再帰型で使われています。
if(s==100)return;を入れた理由は、すべて計算させるには時間がかかりすぎますので、
100個目で止めるためです。
h=1;
if(g>0){
for(j=0;j<g;j++){
if(a[y[g]][x[g]]==a[y[j]][x[j]]){
h=0;
break;
}
}
}
では、例えば、g=5のときは
1 | 2 | 3 |
4 | 5 | 1 |
* | * | * |
1と他が比べられます。同じものがあるときは、
h=0とすることによって強制的に
if(h==1){
if(g+1<9){
f(g+1);
}
else{
for(j=0;j<3;j++){
dataGridView1->Rows->Add();
for(k=0;k<3;k++){
dataGridView1[k,j+4*s]->Value=a[j][k];
}
}
dataGridView1->Rows->Add();
s++;
}
}
をスルーさせています。そして、ループが進み
1 | 2 | 3 |
4 | 5 | 6 |
* | * | * |
と問題をクリアさせています。
if(g+1<9){
f(g+1);
}
の部分はg+1が8までしか進められません。
0 | 1 | 2 |
3 | 4 | 5 |
6 | 7 | 8 |
セル番号は右図をご覧なればお分かりのように8までしかないからです。
大事な点を繰り返します。
順列のときと同じでセルのラベル(すなわちセル番号)であるgとセルに入っている数字(セルの内容)を明確に区別して下さい。
上図の右側のiはfor(i=0;i<9;i++)のiに対応し、gはvoid f(char g)のgです。
0 | 1 | 2 |
3 | 4 | 5 |
6 | 7 | 8 |
セル番号は8でしかありませんので、
g+1が9のときは、
else{
for(j=0;j<3;j++){
dataGridView1->Rows->Add();
for(k=0;k<3;k++){
dataGridView1[k,j+4*s]->Value=a[j][k];
}
}
dataGridView1->Rows->Add();
s++;
}
が実行されて、出来た方陣がDataGridViewに表示されます。
ここで課題です。現在
と使われない部分が多いので
実行結果が
となるように変更するにはどうしたらよいでしょうか。
今回のプログラミングがご理解できないときは、VC++講義の
第16講 関数の再帰的呼び出しによる3次・4次魔方陣の作成☆
第1話
魔方陣や数独はなぜ関数の再帰的呼び出しに向いているのか
第2話
ソース例
第3話 小改良と実験
第4話 ソース解説その1
第5話 ソース解説その2
第6話 ソース解説その3
第7話 ソース解説その4
を参照して下さい。
第1話へ 第3話へ