2010年6月16日水曜日

ナンバープレース改訂版

その後、改訂や追加で遊んでいましたがほぼ終わりなので、公開します。eclipseを使っています。galileoになって使いやすくなりました。これが無料とはありがたいことです。入出力はJSPに任せて解析部分をjavaで作りました。test用にmain()を書いてあるのでeclipseで直接runできます。
*インデントが無くなって見づらくなりました。
/*
* ナンバープレースの解を探すプログラム
* HMIはJSPを使う
* 動作試験用にmain()で問題を解く

net で難問を探して解法を検討
2択の総当たりで解を探す。16528回 306:2択やり直しで解
*/

package numb;

import java.text.DecimalFormat;

public class NumAa {

int[][] numb;//問題
int[][] numr = new int[9][9];//解析用
int[][] numc = new int[9][9];//変化の比較用
int[][] numsave = new int[9][9];//numrの待避用
int[][][] result = new int[9][9][9];//セル毎の予測値
int[][][] resultsave = new int[9][9][9];//resultの待避用

int[] mark;//判定用のマーク

int count=0;//loopカウンター
int b,c;//blank compare
int e,e1,e2;//error check
int p,q,r,s;
int i,j,k;

int[][][] jph = new int[500][9][9];
int h;//jph:page number
int f;//insert number
int ss;
boolean sf;

public int[][] NumAc(int[][] numb){

//問題を解析用と比較用にコピー
for (i=0; i<9; i++)
for (j=0; j<9; j++)
numc[i][j] = numr[i][j] = numb[i][j];
//分岐履歴をクリアー
for (i=0; i<81; i++)
for (j=0; j<9; j++)
for (k=0; k<9; k++)
jph[i][j][k] = 0;
f=h=0;

//問題を表示
qdsp();

anal:
do{
// System.out.println("\nloop:"+ count);//回数表示
rescl();//resultをクリアー
numan();//解析
// resdsp();//結果表示

for (p=0; p<9; p++){
for (q=0; q<9; q++){
for (s=r=0; r<9; ++r)
if (result[p][q][r] != 0) s++;
//候補が1つ
if (s==1){
//System.out.println("\ns1loop:"+ count);//回数表示
numr[p][q] = result[p][q][0];
//System.out.println(">>decide["+ p +"]["+ q +"]"+numr[p][q]);
rescl();//resultをクリアー
numan();//解析
//e = numchk(1);//矛盾検査(表示無し)結果をeに
//変化をチェック、ブランクを計数&解析結果を比較用にコピー
compr();
continue anal;//do while loopを最初から

}//s1 block
}//q loop
}//p loop

for (p=0; p<9; p++){
for (q=0; q<9; q++){
for (s=r=0; r<9; ++r)
if (result[p][q][r] != 0) s++;
//候補が2つ
if (s==2){
System.out.println("\ns2loop:"+ count);//回数表示

ss = (h>>>f) % 2;//hの数値のfbit目をssへ
if(ss == 0) sf = true;
else sf=false;

if(sf) numr[p][q] = result[p][q][0];
else numr[p][q] = result[p][q][1];

jph[h][p][q] = ++f;
System.out.println(">>decide["+ p +"]["+ q +"]"+numr[p][q]+" f="+ f);
rescl();//resultをクリアー
numan();//解析
//e = numchk(0);//矛盾検査(表示付き)結果をeに
//resdsp();//結果表示
//変化をチェック、ブランクを計数&解析結果を比較用にコピー
compr();
continue anal;//do while loopを最初から

}//s=2 block
}//q loop
}//p loop
// jphdsp();
compr();//変化をチェック
System.out.println("\nc,b="+c+","+b);
if (c==81 && b!=0){//変化は無いけれどブランクが残っている
System.out.print("\n//再度"+h);
for (i=0; i<9; i++)
for (j=0; j<9; j++)
numc[i][j] = numr[i][j] = numb[i][j];
//問題を表示
qdsp();
if (h<500) h++;
f=0;
}
}while ((count++ < 30000) && (b != 0) && (h < 500));//制限回数以下、まだ終わっていない
//continueでanalへ飛ぶがwhileの条件は有効!

resdsp();//結果表示
numchk(0);//結果検査&表示
// jphdsp();//履歴表示

System.out.print("\ncount="+count+" blank="+b+" page="+h);
return numr;
}

//
//履歴表示
void jphdsp(){
int i,j,k;
// double n;
// String fn;

DecimalFormat df = new DecimalFormat("00");
System.out.print("\n履歴を表示\n");
for (i=0; i<9; i++){
for (j=0; j<=h; j++){
for(k=0; k<9; k++){
// fn = String.valueOf(jph[j][i][k]);
// n = Double.parseDouble(fn);
System.out.print(df.format(Double.parseDouble(String.valueOf(jph[j][i][k]))) +",");
}
System.out.print("|");
}
System.out.print("\n");
}
}

//矛盾検査
int numchk(int f){
int[] markc = new int[10];
int i,j,k;
int[][] e = new int[6][9];

for (i=0; i<6; i++){//判定結果をクリアー
for (j=0; j<9; j++){
e[i][j] = 0;
}
}
//行判定
for (j=0; j<9; j++){//行選択
for (i=0; i<10; i++)//判定用のmarkc[0..9]に0-9を入れる
markc[i] = i;
for (i=0; i<9; i++){
if (numr[j][i] == 0){//ブランクをカウント
e[0][j]++;
}
else {//ブランクで無ければ
if (markc[numr[j][i]] == 0)//0が書いてあれば
e[3][j]++; //だぶり
markc[numr[j][i]] = 0;
}
}
}
//列判定
for (j=0; j<9; j++){//列選択
for (i=0; i<10; i++)//判定用のmarkc[0..9]に0-9を入れる
markc[i] = i;
for (i=0; i<9; i++){
if (numr[i][j] == 0){
e[1][j]++;
}
else {
if (markc[numr[i][j]] == 0)//0が書いてあれば
e[4][j]++; //だぶり
markc[numr[i][j]] = 0;
}
}
}
//ブロック判定
for (j=0; j<9; j++){//ブロック選択
for (i=0; i<10; i++)//判定用のmarkc[0..9]に0-9を入れる
markc[i] = i;
for (i=0; i<9; i++){

//j i 0 1 2 3 4 5 6 7 8
//0: 00 01 02 10 11 12 20 21 22
//1: 03 04 05 13 14 15 23 24 25
//2: 06 07 08 16 17 18 26 27 28
//3: 30 31 32 40 41 42 50 51 52
//4: 33 34 35 43 44 45 53 54 55
//5: 36 37 38 46 47 48 56 57 58
//6: 60 61 62 70 71 72 80 81 82
//7: 63 64 65 73 74 75 83 84 85
//8: 66 67 68 76 77 78 86 87 88

//j/3*3 0,0,0,3,3,3,6,6,6
//j%3*3 0,3,6,0,3,6,0,3,6

//i/3 0,0,0,1,1,1,2,2,2
//i%3 0,1,2,0,1,2,0,1,2

if (numr[j/3*3+i/3][j%3*3+i%3] == 0) {
e[2][j]++;

}
else {
if (markc[numr[j/3*3+i/3][j%3*3+i%3]] == 0)//0が書いてあれば
e[5][j]++; //だぶり
markc[numr[j/3*3+i/3][j%3*3+i%3]] = 0;
}
}
}
if(f == 0){
System.out.print("\n矛盾検査:");
for (i=0; i<6; i++){
k=0;
System.out.print(" ");
for (j=0; j<9; j++){
System.out.print(e[i][j]);
k=k+e[i][j];
}
System.out.print(":"+k);
}
System.out.print("\n");
}
k=0;
for (i=3; i<6; i++){
for (j=0; j<9; j++){
k=k+e[i][j];
}
}
return k;
}

//問題表示
void qdsp(){
int i,j;
System.out.println("\n問題を表示");
for (i=0; i<9; i++){
for (j=0; j<9; j++)
System.out.print(numr[i][j]+",");
System.out.print("\n");
}
}
//結果表示
void resdsp(){
int i,j,k,l;
// System.out.println("\n結果表示");
for (i=0; i<9; i++){
for (j=0; j<9; j++)
System.out.print(numr[i][j]+",");
System.out.print("..");
for (j=0; j<9; j++){
System.out.print("/");
l=0;
for (k=0; k<9; k++){
if (result[i][j][k] != 0){
System.out.print(result[i][j][k]+".");
l++;
}
}
System.out.print("("+l+")");
}
System.out.print("\n");
}
System.out.print("\n");
}
//結果の配列をクリアー
void rescl(){
int i,j,k;
// System.out.println("結果の配列をクリアー");
for (i=0; i<9; i++)
for (j=0; j<9; j++)
for (k=0; k<9; k++)
result[i][j][k]=0;
}
//結果の配列を待避
void ressav(){
int i,j,k;
for (i=0; i<9; i++)
for (j=0; j<9; j++)
for (k=0; k<9; k++)
resultsave[i][j][k]=result[i][j][k];
}
//待避した結果の配列を戻す
void resvcal(){
int i,j,k;
for (i=0; i<9; i++)
for (j=0; j<9; j++)
for (k=0; k<9; k++)
result[i][j][k]=resultsave[i][j][k];
}
//解析用の配列を待避
void numsav(){
int i,j;
for (i=0; i<9; i++)
for (j=0; j<9; j++)
numsave[i][j]=numr[i][j];
}
//待避した解析用の配列を戻す
void numvcal(){
int i,j;
for (i=0; i<9; i++)
for (j=0; j<9; j++)
numr[i][j]=numsave[i][j];
}
//変化をチェック、ブランクを計数&解析結果を比較用にコピー
void compr(){
int i,j;
// System.out.println("変化をチェック、解析結果を比較用にコピー");
b=c=0;//b=0でブランクが無し、c=81で変化無し
for (i=0; i<9; i++)
for (j=0; j<9; j++){
if (numc[i][j] == numr[i][j])
c++;
if (numr[i][j] == 0)
b++;
}

for (i=0; i<9; i++)
for (j=0; j<9; j++)
numc[i][j] = numr[i][j];
}

//numrを解析して候補をresultに書く
void numan(){

int[] mark = new int[10];//判定用のマーク
int m,n,o;
int i;

for (m=0; m<9; m++)//行を指示
for (n=0; n<9; n++)//列を指示
if (numr[m][n] == 0){//評価数値が0なら判定作業
for (i=0; i<10; i++)//判定用のmark[0..9]に0-9を入れる
mark[i] = i;
//行判定
for (i=0; i<9; i++)//markの該当位置に0を入力
mark[numr[m][i]]=0;
//列判定
for (i=0; i<9; i++)//markの該当箇所に0を入力
mark[numr[i][n]]=0;
//ブロック判定
for (i=0; i<9; i++)//数値が有ればmarkの該当箇所に0を入力
mark[numr[m/3*3+i/3][n/3*3+i%3]]=0;
//推定される数値の数が1なら確定
for (o=i=0; i<10; i++)//マークの空白=0以外を数える
if (mark[i] != 0)
result[m][n][o++]=mark[i];
}//判定
//n-loop
//m-loop
}

//動作試験用
public static void main(String[] args){
int[][] numb = {
{0,2,0,0,0,0,0,7,0},
{1,0,0,0,4,0,0,0,2},
{0,0,3,0,8,0,4,0,0},
{0,0,0,4,0,6,0,0,0},
{0,9,4,0,0,0,2,3,0},
{0,0,0,3,0,8,0,0,0},
{0,0,5,0,6,0,8,0,0},
{2,0,0,0,3,0,0,0,4},
{0,7,0,0,0,0,0,5,0}};


NumAa num = new NumAa();
num.NumAc(numb);

}

}