ナンプレ(数独)の解の有無のプログラム

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
TG

ナンプレ(数独)の解の有無のプログラム

#1

投稿記事 by TG » 16年前

大学で4×4ナンプレ(数独)が解けるかどうか、又、解ける場合解を印字するプログラム(最近似たような記事があったようですが。)を書けという課題が出ました。
手も足も出なかったためネット上で検索したところ
http://motonaga.hp.infoseek.co.jp/Sudoku/Sudoku.html
上記のページを見つけました。
しかし、私がポインタを使いこなせるレベルではなく、,main関数内の意味も分からなかったので、とりあえず真似だけでもと思い、他の関数を参考に以下のプログラムを作りました。
#include<stdio.h>
#define BUFLEN 80
int initial[4][4],solve[4][4],x,y;

int copy(void){//1~4以外の数値の時空欄(0)と置き直す関数
	int i,j;
	for(i=0;i<4;i++){
		for(j=0;j<4;j++){
			if(
				initial[j]==1||initial[j]==2||
				initial[j]==3||initial[j]==4
				){
					solve[j]=initial[j];
			}else{solve[j]=0;
			}
		}
	}
return 0;
}


int print_answer(void){//答えの印字の関数
	int i,j;
	for(i=0;i<4;i++){
		for(j=0;j<4;j++){
			printf("%2d",solve[j]);}
		printf("\n");
	}
	printf("\n");
	return 0;
}

int blank_search(){//空いた枡を見つける
    int i,j;
	for(i=0;i<4;i++){
        for(j=0;j<4;j++)
            if (solve[j] == 0){
                x=i; y=j;
                return 1;
			}
	}
    return 0;
}

int permit_puting(int k){
	int i,j;
	for(i=0;i<4;i++){ /* 縦に同じ数はないか */
		if(solve[y] == k){return 0;}
	}
	for(j=0;j<4;j++){/* 橫に同じ数はないか */
		if(solve[x][j] == k){return 0;}
	}
	for(i=0;i<2;i++){/* blockに同じ数はないか */
		for(j=0;j<2;j++){
			if(solve[2*(x/2)+i][2*(y/2)+j]==k){return 0;}
			}		
	}
	return 1;
}

int trial_error(int solve[4][4]){
	int k;
	if (blank_search()==1){/* 盤に空いた枡(x,y)があるか */
		for(k=1;k<=4;k++){
			if(permit_puting(k)==1){/* 枡(x,y)にkを置けるか */
				solve[x][y] = k; /* 置けるなら置く */
				//print_answer();
				trial_error(solve);      /* 次を確かめる */
				solve[x][y] = 0; /* 枡(x,y)にkを置けないとして別の置き方はないか */
			}
		}
	}else{
		print_answer();
	}
	return 0;
}

int number_place(){//未完成(最終的にmainに記述された分岐のために0か1を返すように作る予定)
	
	copy();
	trial_error(solve);




return 0;
}

//以下は変更できない
int main(void){
	char buf[BUFLEN];
	int i;
	for(i=0;i<4;i=i+1){
		fgets(buf,BUFLEN, stdin);
		sscanf(buf,"%d %d %d %d",
			&initial[i][0],&initial[i][1],
			&initial[i][2],&initial[i][3]);
	}

	if (number_place()==1){
		printf("solved.\n");
	}else{
		printf("not solved");
	}

	return 0;
}

コンパイルは可能です。またいくつかの問題も解くことはできるのですが、すべて空欄の場合解が1つしかでないなど欠陥があるようです。
trial_error内のprint_answerを実行させたところ、どうも再帰がうまくいってないような気がします。しかし改善方法も分からない状態です。
上記の通り、できればポインタは使いたくありませんが、ポインタを使わなければいけないようでしたら再考してみます。
Visual C++2008を使っています。

初投稿で至らない点があるとは思いますがよろしくお願いします。

たいちう

Re:ナンプレ(数独)の解の有無のプログラム

#2

投稿記事 by たいちう » 16年前

参考元のTry()とあなたのtrial_error()を比較してください。
Try()はローカル変数として、xとyを使用していますよね。
Try()が再帰関数として10回呼ばれたとすると、
x, yも10組の独立した変数が用意されます。

あなたのプログラムでは、xとyはグローバル変数ですので、
trial_error()が複数回呼ばれたとき、xとyはどんどん更新されてしまい、
再帰から戻っても元の値を取り出せません。
これが再帰が機能していない原因です。

直し方はTry()の用にローカル変数にすることで、
この変数をblank_search()やpermit_puting()で使えるようにするには、
これもTry()で行っているように、ポインタを渡すのが一番簡単でしょう。
(お手本もあることですし)

ポインタの学習をしましょう。
ポインタを避ける工夫が、アルゴリズムを破壊してしまっています。

TG

Re:ナンプレ(数独)の解の有無のプログラム

#3

投稿記事 by TG » 16年前

回答ありがとうございます
やはりポインタを使うのが得策なのですね。

>ポインタの学習をしましょう。
今後のためにも、頑張って身につけたいと思います。

今回は本当にありがとうございました。
また何かありましたら、ご指導のほどよろしくお願いします。

閉鎖

“C言語何でも質問掲示板” へ戻る