R南付属中数研98文化祭問題
10月3、4日に某R南高校ならびに同付属中学校の文化祭がありました。4日の午前に中学校の方の数学研クラブ(以下数研)の展示を見に行ったんですけど、そこで算数パズル的な問題(景品付き)が置いてありました。ぱっと見い、これは、ニューラルネットで一発じゃねえかと思って、プログラムにかかりました。どうして、プログラムできたかというと、ノートパソコン何か持ってるわけはなく、実はというか、自分は、R南高校の3年で理科部に所属していて、そこでできたんです。激貧マシン386で。プログラムは、なんとか2、3時間で完成し、あとは、実行あるのみ。2時間ぐらい走らしてたんですけど、解は、出ず。景品はもらえずタイムオーバー。あとで聞いた話なんですけど、この景品ってやつは、謎の音楽CDらしいです。いらねー。
まあ、後になって中途半端で終わらすのもいやだったんで、ソースを見直してたら、これがまたひどい。解が出るわけねえよこんなの的なミスを連発。ちょこちょこっといじくって完成はしましたけど。
お話しは、これぐらいにしといてNNいっときましょ。
問題
次のような9×9のマス目を考えます。
そして、1〜9の数字を縦横に一つずつ入れていき、さらに太線で囲まれた3×3の9マスそれぞれにも1〜9の数字がくるようにする問題です。実際の問題には、あらかじめいくつかの数字が書かれており、それをもとに解いていきます。
ニューラル表現
V[9][9][9]、U[9][9][9]を用意します。なぜ、三次元かといいますと、問題のマス目を9個重ねたものを今考えてください。そして、3段目の(5,6)が発火していたとします(V[5][6][3]=1)。この場合、(5,6)に3が入ることを表します。何段目が発火してるかでそこに入る数字を決定します。そして、リクエスト(はじめから決められている数字)の為のR[9][9]の配列を用意します。これに、(2,9)が5だとすると、R[2][9]=5としてやります。
動作式
第1、2、3項は、おなじみで横、縦、奥(段)にそれぞれニューロンが1つ発火するようにします。第4項は、囲いの条件です。同じ段のある囲いに1つのニューロンが発火するようにします。第5項からは、ヒルクライミングタームです。
プログラミング
リクエストがあると、その行、列、奥、その段での囲いのニューロンは固定されてしますのでそれらを判断するのにF[9][9][9]の三次元配列を用意します。
/*フラグをすべて立てる*/
for ( z = 1; z <= 9; z++ )
{
for ( y = 1; y <= 9; y++ )
{
for ( x = 1; x <= 9; x++ )
{
F[x][y][z] = 1;
}
}
}
y = 1;
while ( y <= 9 )
{
x = 1;
while ( x <= 9 )
{
if ( R[x][y] < 0 || R[x][y] > 9 ) /*リクエストが不正*/
return INIT_ERROR;
else if ( R[x][y] != 0 ) /*リクエストがあるか*/
SetF( x, y, R[x][y] );
x++;
}
y++;
}
void SetF( int x, int y, int z )
{
int i, j;
int nx, /*囲いのx座標*/
ny; /*囲いのy座標*/
V[x][y][z] = 1;
nx = (int)((x - 1) / 3) * 3 + 1;
ny = (int)((y - 1) / 3) * 3 + 1;
/*横のフラグを倒す*/
for ( i = 1; i <= 9; i++ )
F[i][y][z] = 0;
/*縦のフラグを倒す*/
for ( i = 1; i <= 9; i++ )
F[x][i][z] = 0;
/*奥のフラグを倒す*/
for ( i = 1; i <= 9; i++ )
F[x][y][i] = 0;
/*囲いのフラグを倒す*/
for ( j = ny; j <= ny + 2; j++ )
for ( i = nx; i <= nx + 2; i++ )
F[i][j][z] = 0;
}
として、フラグを設定し、後は、次のようにして動作式をぐるぐるさせていきます。
void MainProcess()
{
int x, y, z;
for ( x = 1; x <= 9; x++ )
{
for ( y = 1; y <= 9; y++ )
{
for ( z = 1; z <= 9; z++ )
{
if ( F[x][y][z] )
{
U[x][y][z] += dU( x, y, z );
if ( U[x][y][z] > UMAX )
U[x][y][z] = UMAX;
if ( U[x][y][z] < -UMAX )
U[x][y][z] = -UMAX;
if ( U[x][y][z] > UTP )
V[x][y][z] = 1;
if ( U[x][y][z] < LTP )
V[x][y][z] = 0;
}
}
}
}
}
ソースについて
リクエストは、次のような形式のファイルを作ってください。
0,0,0,2,0,0,0,7,8
0,0,9,0,3,0,0,0,4
0,3,0,0,0,5,0,0,0
4,0,0,0,6,0,8,0,0
0,1,0,3,0,2,0,5,0
0,0,2,0,1,0,0,0,6
0,0,0,6,0,0,0,9,0
8,0,0,0,5,0,1,0,0
9,4,0,0,0,3,0,0,0
リクエストがあるところは、その数字をまた、ないところは0を”,”で区切って1行ずつ書いてください。そして、実行するときは、ドラッグするなりして、ファイル名を渡してください。指定しなければ、実行時に聞いてきます。
suken98.c ソースファイル
suken98.exe 実行形式ファイル
skn98_01.dat 問題ファイル1
skn98_03.dat 問題ファイル2
ソースのヘッダに某Rの謎が解ける!?
この問題を自力で解いた人がいるんですけど、2問あってskn98_03.datの方は、1、2時間で解けるらしいですけど、skn98_01.datの方は、4、5時間かかったとか。数研もこんな時間かかる問題を2問も出すなんていやらしいすぎるわ。まあ、プログラム作ってやるのと時間的には、いい勝負かも。しかし、自分は、こんな問題に何時間も費やしたくない。ニューラルネット様々。パソコン様々。
次回は、何をやるか決めていないんですけど、
君も、ニューラルネットワーク伝説にヒュージョンせよ!