メニエスの名のもとに

プログラミング関係を中心としたぐだぐだブログ

ナンプレ(数独)問題作成プログラム その13

前回のプログラムを実行出来るように solve.c の main を変更する。

たいした変更じゃないのでMSaito/NumPl · GitHubを見て。
で、実行すると、

./solve -r "  6 31 9   9   427 7         1  6     7   8     5  2         5 143   9   8 24 61 "
quiet = 0
recursion = 1
argc = 1
??6?31?9?
??9???427
?7???????
??1??6???
??7???8??
???5??2??
???????5?
143???9??
?8?24?61?
has more than 2 solution
??6?31?9?
??9???427
?7???????
??1??6???
??7???8??
???5??2??
???????5?
143???9??
?8?24?61?
426731598
319865427
578492136
251976345
937124861
864583279
692318754
143657982
785249613

とかいう結果が出ておかしい。困ったことに、recursion_solve がおかしいという可能性と、これまでのsolveがおかしいという可能性がある。だが、上の出力をじっと見つめると、

864583279

という行があり、8が2回出てくるので間違い。つまり、recursion_solveに問題があることは間違いない。そこでいろいろ調べたりするのだが、実は今回このブログで発表するためにかっこよく変更した部分があって、そこのバグなのであった。
それは、kill_single.c の kill_line 関数である。forループを2回まわるだけで行内のsingleを消せるという部分である。その最初のforループ

    for (int i = 0; i < LINE_SIZE; i++) {
	int idx = line[i];
	if (is_single(ar[idx])) {
	    syms |= ar[idx].symbol;
	}
    }

上のプログラムだと、さっきのように行内に8が二つあるような場合でも、そのまま見過ごしてしまう。そこで次のように変更すると同じ数字が二つある場合にチェックできる。(count の使い回しが気になるが、count していることは確かなので別の変数を取る必要はないかなと思う)

    for (int i = 0; i < LINE_SIZE; i++) {
	int idx = line[i];
	if (is_single(ar[idx])) {
	    syms |= ar[idx].symbol;
	    count++;
	}
    }
    if (count != ones16(syms)) {
	return -1;
    }
    count = 0;

もうひとつの問題点は、recursion_solve が解を返さないことなので、解を返すように修正。実行すると、

$ ./solve -r "  6 31 9   9   427 7         1  6     7   8     5  2         5 143   9   8 24 61 "
quiet = 0
recursion = 1
argc = 1
??6?31?9?
??9???427
?7???????
??1??6???
??7???8??
???5??2??
???????5?
143???9??
?8?24?61?
solved
426731598
319865427
578924136
251486379
637192845
894573261
962318754
143657982
785249613

となる。これでrecursion_solveの動作は確認できた。(本当はもっとテストするべきだけど)

ヤング図形を求めるプログラムを載せようとしてやめた

いや、別のプログラムを載せると、別の人が検索してやってくるかもと思ったのだが、ヤング図形のプログラムで検索した結果、私のプログラムよりスマートらしく見えるプログラムがあったので掲載をやめたのだ。

ヤング図形のはなし (日評数学選書)

ヤング図形のはなし (日評数学選書)