8 queen


8 queenパズル

チェスのクイーンを8x8のチェス盤に8個、互いに取られないように配置する パズル。

クイーンは、

のように八方に動ける。動ける場所に他のクイーンがないような配置とは、 例えば

のようなものである。

このような配置は、全部で92通り、回転や裏返しを除くと12通り存在する。

Javaアプレット

ソースはここ

8 queenパズルを解く

定式化

各列に一つしかクイーンを置けず、また必ず一つクイーンを おくのは明らかなので、その位置を0〜7の数値で表すと、盤面の状態は 8個の数値で表せる。例えば上の盤面は、

(4 0 3 5 7 1 6 2)

という数値で表せる。これをboard[i]という配列に格納すると、 問題は、

を満たすような数字の組を求める問題と見倣せる。

解法

これをいきなり解くのは難しいので、問題を分割する。 すなわち、 のような関数群を用意したと考える。このとき、最後の関数は 簡単なものになろう。n番目の関数は、n+1番目の関数がちゃんと動くならば、 n列目を固定してn+1番目の関数を呼ぶ操作をn列目の全ての可能性について 繰り返せば良い。

再帰呼出し

上のアルゴリズムを実装するには、上記8個の関数を書けばよい (queen0.c)が、 これを見るとほとんど同じような関数ばかりが並んでいることが分かる。 このような書き方ではバグが混入しやすいし、何よりも8クイーンでなく Nクイーンを解くようなプログラムを書くことが出来ない。

そこで、全て一つの関数で書いて、引数でその関数が何番目の関数かを 指定することにする。 こう書いたものが、 queen1.c である。極めて単純になり、かつNクイーンへの拡張も容易になったことが 分かるであろう。

このプログラムは、典型的な「バックトラック法」である。

高速化

上のプログラムで最も時間がかかっていると思われる場所は、 二重ループになっている
for (j=0; j < n; j++) {
	if (board[j] == i) goto label;
	if (j - board[j] == n - i) goto label;
	if (j + board[j] == n + i) goto label;
}
の部分だと思われる。ここの部分を表を引くことによって高速化したものが queen2.c である。実行時間を比較すると、

盤面の大きさ queen1.c queen2.c
10 0.247 0.088
11 1.407 0.373
12 8.585 2.116
13 54.092 11.951

となる。(MMX Pentium 166MHz + FreeBSD)


8 queen / kashi@mn.waseda.ac.jp