数独(sudoku) 日本発祥のパズル プログラミング言語I 第13回 パズル2 ナンバープレース, Single Numbersなど様々 な名称がある 縦9個、横9個のマス目を使用 全てのマスに1~9の1桁の数字を入れる 2 3 4 数独の 問題例 9 1 4 埼玉大学工学部 電気電子システム工学科 伊藤 和人 横1行、縦1行には同じ数字は入らない ボックスにも同じ数字は入らない 7 4 行 6 9 8 1 ボックス 2 3 5 9 8 6 5 7 Copyright © 2008 Kazuhito Ito 8 空きマスに 数字を入れる ここに‘2’があるので、同じボックス、同じ行、 同じ列のマスには‘2’は入れられない 2 3 4 列 答え 2 1 2 いくつかのマスは 予め数字が入っている 解き方1 ルール 7 5 4 1 9 7 2 3 2 5 8 7 4 3 6 1 9 3 1 9 2 5 6 7 8 4 4 7 1 6 2 9 8 5 3 5 6 2 1 3 8 9 4 7 9 8 3 5 7 4 1 6 2 8 2 7 4 1 5 3 9 6 6 3 5 8 9 7 4 2 1 1 9 4 3 6 2 5 7 8 9 1 4 2 1 2 9 8 5 Copyright © 2008 Kazuhito Ito 7 5 4 1 9 7 2 3 ‘2’が入れら れないマス 6 7 8 Copyright © 2008 Kazuhito Ito 解き方1(続き1) 解き方1(続き2) ‘2’を入れられるのは、 このマスだけ ⇒ ここは‘2’に決定 2 3 4 2 3 4 9 1 4 ここに‘2’ 2 1 2 9 8 5 Copyright © 2008 Kazuhito Ito 7 5 4 1 9 7 2 3 ‘2’が入れら れないマス 9 1 2 4 2 1 2 6 7 9 8 8 5 Copyright © 2008 Kazuhito Ito 7 5 4 1 9 7 2 3 6 7 8 ‘2’が入れら れないマス ここに‘2’ 解き方2 考え方 2 3 4 9 1 4 7 5 4 1 9 7 2 3 2 1 2 9 8 5 注目するマスと 同一行、同一列、 同一ブロックの マス 6 7 8 ‘3’以外は、行、列、ボックスのどこかに存在 ⇒ このマスには‘3’しか入れられない ⇒ ‘3’に決定 Copyright © 2008 Kazuhito Ito マスに関するデータを記録する構造体 typedef struct PlaceRec{ int bFixed; int nValue; 要素aValue[1]~ int aValue[10]; aValue[9]を利用し、 } PLACE; aValue[0]は使用しない int bFixed; マスの値が決定しているか否か int nValue; 決定したマスの値 データ構造と初期化 9×9個のマスについて構造体を用意 PLACE Place[9][9]; レベル1の推論 ルールをそのまま適用して値の除外、 値の決定ができるもの Copyright © 2008 Kazuhito Ito 初期化 int x, y, v; for( x=0 ; x<9 ; x++ ){ for( y=0 ; y<9 ; y++ ){ Place[x][y].bFixed = 0; 値は未定 for( v=1 ; v<=9 ; v++ ){ Place[x][y].aValue[v] = 1; } } 1~9の全ての値が入れられる } Copyright © 2008 Kazuhito Ito 構造体メンバaValue[10]の意味 v=1~9について aValue[v]=1 値vがマスに入れられる aValue[v]=0 値vがマスに入れられない 例えば aValue[1]=aValue[2]=・・・aValue[9]=0 aValue[4]のみ1の場合、 構造体のメンバ int aValue[10]; 1~9の各値vが マスに入れられるか否か Copyright © 2008 Kazuhito Ito 「ここ」に入りえない値を除外する 「ここ」にしか入れられない値を見つける データ構造(続き) データ構造 基本は、各マスには全ての値が入りうる マスの値は4と決定できる 他にも値の決め方あり(後述) Copyright © 2008 Kazuhito Ito データ初期化(続き) 予めマスに入っている値を設定 マス(1,0) 2 3 4 9 1 void FixOne(int x, int y, int v0) { int v; for( v=1 ; v<=9 ; v++ ){ Place[x][y].aValue[v] = 0; } Place[x][y].aValue[v0] = 1; Place[x][y].bFixed = 1; Place[x][y].nValue = v0; } v0以外は 除外 値をv0に FixOne( 1, 0, 2 ); /* マス(1,0)は2 */ その他のマスについても同様に初期化する Copyright © 2008 Kazuhito Ito マスの値を限定する(その1) 「ここ」に入りえない値を除外する 4 1 2 行y 2 5 9 このマスが値5に 決定した場合 1 9 7 2 行yの他のマスには 3 値5は入らない void L1InferRow(int y, int v) 行方向の推論 { int i; for( i=0 ; i<9 ; i++ ){ if( Place[i][y].bFixed ) continue; Place[i][y].aValue[v] = 0; 行yのマス(i,y)から } 値vを除外 } Copyright © 2008 Kazuhito Ito マスの値を限定する(その3) ボックス内の推論 void L1InferBox(int bx, int by, int v) { int i,j; for( i=0 ; i<3 ; i++ ){ for( j=0 ; j<3 ; j++ ){ if( Place[bx+i][by+j].bFixed ) continue; Place[bx+i][by+j].aValue[v] = 0; } ボックス(bx,by)内の } } マスから値vを除外 ここまで、レベル1の推論 Copyright © 2008 Kazuhito Ito 考え方 基本は、各マスには全ての値が入りうる 「ここ」に入りえない値を除外する 「ここ」にしか入れられない値を見つける レベル1の推論 ルールをそのまま適用して値の除外、 値の決定ができるもの Copyright © 2008 Kazuhito Ito マスの値を限定する(その2) void L1InferColumn(int x, int v) 列方向の推論 { int j; for( j=0 ; j<9 ; j++ ){ if( Place[x][j].bFixed ) continue; Place[x][j].aValue[v] = 0; 列xのマス(x,j)から } 値vを除外 } Copyright © 2008 Kazuhito Ito マスの値を限定する(その4) レベル1の推論を適用 void L1Infer() { int bx, by, i, j, v; for( bx=0 ; bx<9 ; bx+=3 ) for( by=0 ; by<9 ; by+=3 ) マス(bx+i,by+j)は for( i=0 ; i<3 ; i++ ) 値vに確定している for( j=0 ; j<3 ; j++ ) if( Place[bx+i][by+j].bFixed ){ v = Place[bx+i][by+j].nValue; L1InferBox( bx, by, v ); // ボックス L1InferRow( by+j, v ); // 行 L1InferColumn( bx+i, v ); // 列 } } Copyright © 2008 Kazuhito Ito マスの値を決める(その1) 唯一入れられる値に決定する for( x=0 ; x<9 ; x++ ){ マス(x,y)について for( y=0 ; y<9 ; y++ ){ if( Place[x][y].bFixed ) continue; k = 0; for( v=1 ; v<=9 ; v++ ){ 値1~9のうち if( Place[x][y].aValue[v] ){ k++; v0 = v; マスに入れられる } 値を数える } if( k == 1 ){ /*マス(x,y)に入り得る値は唯一*/ FixOne( x, y, v0 ); 唯一入れられる } 値v0に決定する } } Copyright © 2008 Kazuhito Ito マスの値を決める(その2) 「ここ」にしか入れられない値を見つける /* 行方向 */ 値vについて for( y=0 ; y<9 ; y++ ){ k = 0; for( x=0 ; x<9 ; x++ ){ 行yのマスのうち if( Place[x][y].aValue[v] ){ 値vを入れられる k++; x0 = x; マスを数える } } if( k == 1 ){ /* 行yで値vを入れられるマスは唯一 */ if( ! Place[x0][y].bFixed ){ FixOne( x0, y, v ); マス(x0,y)を } 値vに決定する } } Copyright © 2008 Kazuhito Ito マスの値を決める(その4) 「ここ」にしか入れられない値を見つける /* ボックス */ 値vについて for( bx=0 ; bx<9 ; bx+=3 ){ for( by=0 ; by<9 ; by+=3 ){ ボックス内で k = 0; 値vを入れられる for( i=0 ; i<3 ; i++ ) マスを数える for( j=0 ; j<3 ; j++ ) if( Place[bx+i][by+j].aValue[v] ){ k++; x0 = bx+i; y0 = by+j; } ボックス内で値vを } 入れられるマスは唯一 if( k == 1 ) if( ! Place[x0][y0].bFixed ) マス(x0,y0)を FixOne( x0, y0, v ); 値vに決定する } Copyright © 2008 Kazuhito Ito 実行例 マスの値を決める(その3) Copyright © 2008 Kazuhito Ito マスの値を決定する 値を確定するとともに推論を行う ⇒ FixOne関数を修整 void FixOne(int x, int y, int v0) { int v; for( v=1 ; v<=9 ; v++ ){ Place[x][y].aValue[v] = 0; } Place[x][y].aValue[v0] = 1; マス(x,y)の値確定 Place[x][y].bFixed = 1; Place[x][y].nValue = v0; 確定した値はv0 } L1Infer(); レベル1の推論を行う Copyright © 2008 Kazuhito Ito マスの値を限定する(その5) 開始前 「ここ」にしか入れられない値を見つける /* 列方向 */ 値vについて for( x=0 ; x<9 ; x++ ){ k = 0; for( y=0 ; y<9 ; y++ ){ 列xのマスのうち if( Place[x][y].aValue[v] ){ 値vを入れられる k++; y0 = y; マスを数える } } if( k == 1 ){ /* 列xで値vを入れられるマスは唯一 */ if( ! Place[x][y0].bFixed ){ FixOne( x, y0, v ); マス(x,y0)を } 値vに決定する } } 「ここ」に入りえない値を除外する ①このボックス では値5は 3 2 ここに入る 1 8 6 1回目の ルール適用 ◎ここが5に 決まった ②このボックスでは 値5はこれらのマスには入らない 2回目の ルール適用 レベル2の推論 Copyright © 2008 Kazuhito Ito 4 5 1 9 7 2 マスの値を限定する(その6) 行方向処理 値vについて k = 0; ボックス(bx,by)内の行jに for( j=0 ; j<3 ; j++ ){ 値vが入る可能性がある for( i=0 ; i<3 ; i++ ) if( Place[bx+i][by+j].aValue[v] ) break; if( i<3 ){ 値vが入りうる行を数える j0 = j; k++; } ボックス(bx,by)内で、値vは } 行j0にのみ入る可能性がある if( k == 1 ){ for( bx0=0 ; bx0<9 ; bx0+=3 ){ if( bx0 == bx ) continue; for( i=0 ; i<3 ; i++ ) Place[bx0+i][by+j0].aValue[v] = 0; } 横に並んだ他のボックスでは } 行j0に値vは入ってはならない Copyright © 2008 Kazuhito Ito マスの値を限定する(その8) 「ここ」に入りえない値を除外する void L2Infer() レベル2の推論 { int bx, by, bx0, by0; int i,j,i0,j0; int v, k; for( by=0 ; by<9 ; by+=3 ){ for( bx=0 ; bx<9 ; bx+=3 ){ for( v=1 ; v<=9 ; v++ ){ 行方向の推論(前々ページ) 列方向の推論(前ページ) } } } } Copyright © 2008 Kazuhito Ito 改良版実行例 マスの値を限定する(その7) 列方向処理 値vについて k = 0; ボックス(bx,by)内の列iに for( i=0 ; i<3 ; i++ ) 値vが入る可能性がある for( j=0 ; j<3 ; j++ ){ if( Place[bx+i][by+j].aValue[v] ) break; if( j<3 ){ 値vが入りうる列を数える i0 = i; k++; } ボックス(bx,by)内で、値vは } 列i0にのみ入る可能性がある if( k == 1 ){ for( by0=0 ; by0<9 ; by0+=3 ){ if( by0 == by ) continue; for( j=0 ; j<3 ; j++ ) Place[bx+i0][by0+j].aValue[v] = 0; } 縦に並んだ他のボックスでは } 列i0に値vは入ってはならない Copyright © 2008 Kazuhito Ito マスの値を決定する 値を確定するとともに推論を行う void FixOne(int x, int y, int v0) { int v; for( v=1 ; v<=9 ; v++ ){ Place[x][y].aValue[v] = 0; } Place[x][y].aValue[v0] = 1; マス(x,y)の値確定 Place[x][y].bFixed = 1; Place[x][y].nValue = v0; 確定した値はv0 } L1Infer(); L2Infer(); レベル1, レベル2の推論を行う Copyright © 2008 Kazuhito Ito まとめ 「数独」を解くプログラム 問題を扱うためのデータ構造を考案 発想、考え方をC言語でプログラム化する 開始前 Copyright © 2008 Kazuhito Ito
© Copyright 2024 Paperzz