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.lzh (16KB)

 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問も出すなんていやらしいすぎるわ。まあ、プログラム作ってやるのと時間的には、いい勝負かも。しかし、自分は、こんな問題に何時間も費やしたくない。ニューラルネット様々。パソコン様々。
次回は、何をやるか決めていないんですけど、

君も、ニューラルネットワーク伝説にヒュージョンせよ!


戻る インデックスへ 次へ