八皇后問題

八皇后問題是一個以國際象棋為背景的問題：如何能夠在 8×8 的國際象棋棋盤上放置八個皇后，使得任何一個皇后都無法直接吃掉其他的皇后？為了達到此目的，任兩個皇后都不能處於同一條橫行、縱列、正斜線或反斜線。八皇后問題可以推廣為更一般的 n 皇后擺放問題：這時棋盤的大小變為 n×n，而皇后個數也變成 n。當且僅當 n = 1 或 n ≥ 4 時問題有解。

歷史

八皇后問題最早是由西洋棋棋手（Max Bezzel）於 1848 年提出。第一個解在 1850 年由弗朗茲・諾克（Franz Nauck）給出。並且將其推廣為更一般的 n 皇后擺放問題。諾克也是首先將問題推廣到更一般的 n 皇后擺放問題的人之一。

在此之後，陸續有數學家對其進行研究，其中包括高斯和康託，1874 年，S. 岡德爾提出了一個透過行列式來求解的方法，這個方法後來又被 J.W.L. 格萊舍加以改進。

1972 年，艾茲格・迪傑斯特拉用這個問題為例來說明他所謂結構化程式設計的能力。他對深度優先搜尋回溯演算法有著非常詳盡的描述 2。

解題方法

八個皇后在 8x8 棋盤上共有 4,426,165,368（64C8）種擺放方法，但皇后間互不攻擊只有 11×8 + 1×4 = 92 個 “可行解”。如果將在旋轉和反射下重合的可行解歸為一種的話，則一共有 12 個 “獨立解”（fundamental solution），具體如下：

一個獨立解通常有 8 個變體（含其最初形式），可以先對其進行旋轉 90°、180° 和 270°，加上其最初形式形成 4 個旋轉變體，再針對一個固定位置比如縱軸進行反射（字母 p 的垂直反射是字母 q），從而得到 8 個變體。但獨立解 10 旋轉 180° 形式與最初形式重合，並且旋轉 270° 形式與旋轉 90° 形式重合，所以只有 4 種變體即自身及其反射和 90° 旋轉及其反射。

獨立解 11 具有額外的性質。

獨立解 3 的旋轉 90° 再垂直反射（效果同於 45° 反射）的變體，體現了階梯狀模式而被稱為 “樓梯解”，n 皇后問題對 n ≥ 4 可透過特定公式得到樓梯解：

如果 n 除以 6 的餘數不是 2 或者 3，則這個列表簡單的就是不大於 n 的所有偶數以及隨後的所有奇數。
否則，寫出偶數和奇數的單獨列表，比如：(2, 4, 6, 8 – 1, 3, 5, 7)。
如果餘數是 2，在奇數列表中交換 1 和 3 並移動 5 到末尾，比如：(3, 1, 7, 5)。
如果餘數是 3，在偶數列表中移動 2 到末尾並在奇數列表中移動 1 和 3 到末尾，比如：(4, 6, 8, 2 – 5, 7, 9, 1, 3)。
將奇數列表附加在偶數列表之後並從左至右的在這些橫行上放置皇后，比如：(a2, b4, c6, d8, e3, f1, g7, h5)。

八皇后問題的 12 個獨立解之間沒有由數學性質決定出的次序，這裡的獨立解次序和所採用的變體是求獨立解的特定搜尋演算法的輸出結果（第一個解在棋盤左下角或左上角有一個皇后），不加以人為調整，比如：將獨立解 3 的樓梯變體放在最前，將獨立解 10 放在最後等等。

解的個數

下表給出了 n 皇后問題的解的個數包括獨立解 U 以及可行解 D 的個數：

可以注意到六皇后問題的解的個數比五皇后問題的解的個數要少。現在還沒有已知公式可以對 n 計算 n 皇后問題的解的個數。

示例程式

4.1 求獨立解

4.1.1 Python 語言

下面採用 Python 語言在致力保持不可變性原則下，生成八皇后問題的 12 個獨立解：

def queens (n):

def q (pl, r):

def place (c):

return r+c not in pl1 and r-c not in pl2

return ((pl0+c, pl1|{r+c}, pl2|{r-c}, pl3-{c})

for c in pl3 if place (c))

def pipeline (pl, i):

for ipl in q (pl, i):

if i+1

這裡的演算法核心環節是生成器委託 yield from。將這段程式碼儲存入 queens.py 檔案中，下面演示其執行結果：

$ python3 ./queens.py

'a1', 'b7', 'c5', 'd8', 'e2', 'f4', 'g6', 'h3'

'a1', 'b7', 'c4', 'd6', 'e8', 'f2', 'g5', 'h3'

'a6', 'b1', 'c5', 'd2', 'e8', 'f3', 'g7', 'h4'

'a4', 'b1', 'c5', 'd8', 'e2', 'f7', 'g3', 'h6'

'a5', 'b1', 'c8', 'd4', 'e2', 'f7', 'g3', 'h6'

'a3', 'b1', 'c7', 'd5', 'e8', 'f2', 'g4', 'h6'

'a5', 'b1', 'c4', 'd6', 'e8', 'f2', 'g7', 'h3'

'a7', 'b1', 'c3', 'd8', 'e6', 'f4', 'g2', 'h5'

'a5', 'b1', 'c8', 'd6', 'e3', 'f7', 'g2', 'h4'

'a5', 'b3', 'c1', 'd7', 'e2', 'f8', 'g6', 'h4'

'a5', 'b7', 'c1', 'd4', 'e2', 'f8', 'g6', 'h3'

'a6', 'b3', 'c1', 'd8', 'e4', 'f2', 'g7', 'h5'

4.1.2 jq 語言

下面採用純函數語言程式設計語言 jq，生成八皇后問題的 12 個獨立解：

def queens (n):

def q: . as $pl

$pl4 as $r

$pl3 as $cl | $cl | . as $c

tostring) as $k0

tostring) as $k1

def place:

($k0 | in ($pl1) | not)

and ($k1 | in ($pl2) | not);

select (place)

$pl0+$c, $pl1+{$k0:null},

$pl2+{$k1:null}, $cl-$c, $r+1;

def pipeline (n):

q | if n > 1 then pipeline (n-1) end;

def toletter:

"abcdefghijklmnopqrstuvwxyz".:.+1;

def fund_solut (f):

def inverse: . as $xl

reduce range (0; n) as $i

(; .+ index ($i));

def variants:

., inverse | map (., reverse)

map (., map (n-1-.))

add);

foreach f as $i

(null, ; .1 as $ml

variants) as $nl

if all ($nl; in ($ml) | not) then

$i, ($ml | .$nl=null)

else

null, $ml end;

.0)

select (. != null);

fund_solut (, , , range (0; n), 0

.0)

to_entries

sort;

queens (8)

這裡的演算法核心環節是管道機制。將這段程式碼儲存入 queens.jq 檔案中，下面演示其執行結果：

$ jq -nc -f ./queens.jq

"a1","b7","c5","d8","e2","f4","g6","h3"

"a1","b7","c4","d6","e8","f2","g5","h3"

"a6","b1","c5","d2","e8","f3","g7","h4"

"a4","b1","c5","d8","e2","f7","g3","h6"

"a5","b1","c8","d4","e2","f7","g3","h6"

"a3","b1","c7","d5","e8","f2","g4","h6"

"a5","b1","c4","d6","e8","f2","g7","h3"

"a7","b1","c3","d8","e6","f4","g2","h5"

"a5","b1","c8","d6","e3","f7","g2","h4"

"a5","b3","c1","d7","e2","f8","g6","h4"

"a5","b7","c1","d4","e2","f8","g6","h3"

"a6","b3","c1","d8","e4","f2","g7","h5"

4.2 求可行解

4.2.1 Python 語言

下面採用 Python 語言基於共享變數，生成八皇后問題的 92 個可行解：

def queens (n):

col = False*n

down = False(n2-1)

up = False(n2-1)

def q (r):

def place (c):

return colc == False == downr+c == upr-c

for c in range (0, n):

if place (c):

colc = downr+c = upr-c = True

yield c

colc = downr+c = upr-c = False

def pipeline (i):

for r in q (i):

if i+1

這裡的演算法核心環節是在生成器之間共享靜態變數。將這段程式碼儲存入 queens.py 檔案中，下面演示其執行結果並提取其 92 個解中的前兩個解：

$ python3 ./queens.py | wc -l

92

$ python3 ./queens.py | sed -n '1,2p'

'a1', 'b7', 'c5', 'd8', 'e2', 'f4', 'g6', 'h3'

'a1', 'b7', 'c4', 'd6', 'e8', 'f2', 'g5', 'h3'

這個演算法在 8 個橫行上執行縱列選擇函式 q (r) 的次數分別為：1, 8, 42, 140, 344, 568, 550, 312，共計 1965 次，q (r) 在每個橫行上針對縱列執行函式 place (c) 的次數都是 8，每橫行的選擇數分別為：8, 64, 336, 1120, 2752, 4544, 4400, 2496，總計 15720 次，其中每橫行的成功存乎最終結果者之數分別為：8, 36, 62, 80, 90, 90, 92, 92，總計 550 個。

可以進一步將其寫為下面的 Python 程式碼，其執行方式和結果同於前述：

def queens (n):

cs = 0*n

cl = *range (0, n)

down = False(n2-1)

up = False(n2-1)

def q (r):

def place (c):

return downr+c == False == upr-c

for i, c in enumerate (cl):

if place (c):

csr = c

del cli

downr+c = upr-c = True

yield None

cl.insert (i, c)

downr+c = upr-c = False

def pipeline (i):

for _ in q (i):

if i+1

這個演算法在 8 個橫行上執行縱列選擇函式 q (r) 的次數分別為：1, 8, 42, 140, 344, 568, 550, 312，共計 1965 次，q (r) 在 8 個橫行上針對縱列執行函式 place (c) 的次數分別為：8, 7, 6, 5, 4, 3, 2, 1，每橫行的選擇數分別為：8, 56, 252, 700, 1376, 1704, 1100, 312，總計 5508 次，其中每橫行的成功存乎最終結果者之數分別為：8, 36, 62, 80, 90, 90, 92, 92，總計 550 個。

下面將其寫為過程式 Python 程式碼，其執行方式和結果同於前述：

def queens (n):

cs = 0*n

cl = *range (0, n)

down = False(n2-1)

up = False(n2-1)

rl = *n

toletter = lambda x: \

'abcdefghijklmnopqrstuvwxyz'x

def q (r):

if r

最後將其寫為指令式 Python 程式碼，其執行方式和結果同於前述：

def queens (n):

cs = 0*n

ps = 0*n

cl = *range (0, n)

down = False(n2-1)

up = False(n2-1)

rl = *n

toletter = lambda x: \

'abcdefghijklmnopqrstuvwxyz'x

r = 0

while r >= 0:

p = psr

if p != 0:

c = csr

cl.insert (p-1, c)

downr+c = upr-c = False

for i, c in enumerate (clp:):

if downr+c == False == upr-c:

csr = c

psr = p+i+1

break

if p != psr:

del clp+i

downr+c = upr-c = True

r += 1

else:

psr = 0

r -= 1

if r == n:

for k, v in enumerate (cs):

rlk = toletter (v)+str (k+1)

print (sorted (rl))

r -= 1

queens (8)

4.2.2 Icon 語言

下面採用 Icon 語言，生成八皇后問題的 92 個可行解：

global n

procedure main

local i, r, s, u, v, x

n := 8

s := "abcdefghijklmnopqrstuvwxyz"

every x := pipeline (1) do {

r :=

every i := 1 to n do put (r, sxi

i)

u := sort (r)

v := get (u)

every v

= ","

u

write (v)

}

end

procedure pipeline (i)

if i

這裡的演算法核心環節是可逆賦值運算。將這段程式碼儲存入 queens.icn 檔案中，下面演示其執行結果並提取其 92 個解中的前兩個解：

$ icon ./queens.icn | wc -l

92

$ icon ./queens.icn | sed -n '1,2p'

a1,b7,c5,d8,e2,f4,g6,h3

a1,b7,c4,d6,e8,f2,g5,h3

4.2.3 C 語言

下面是求解 n 皇后的 C 程式碼，在程式中可以自己設定 n 個皇后以及選擇是否打印出具體解。

include
define QUEENS 8 / 皇后數量 /
define ISOUTPUT 1 /(ISOUTPUT=0 or 1)，Output 用於選擇是否輸出具體解，為 1 輸出，為 0 不輸出 /

int AQUEENS + 1, BQUEENS 3 + 1, CQUEENS 3 + 1, kQUEENS + 1QUEENS + 1;

int inc, a = A, b = B + QUEENS, *c = C;

void lay (int i) {

int j = 0, t, u;

while (++j

使用回溯法進行求解八皇后問題

include
define PRINTF_IN 1 // 定義是否列印，1：列印，0：不列印

int queens (int Queens){

int i, k, flag, not_finish=1, count=0;

// 正在處理的元素下標，表示前 i-1 個元素已符合要求，正在處理第 i 個元素

	int aQueens+1; // 八皇后問題的皇后所在的行列位置，從 1 幵始算起，所以加 1

i=1;

a1=1; // 為陣列的第一個元素賦初值

printf ("% d 皇后的可能配置是:",Queens);

while (notfinish){ //notfinish=l: 處理尚未結束

while (not_finish && i1 && ai==Queens)

ai=1; // 當 ai 為 Queens 時將 ai 的值置 1

else

if (i==1 && ai==Queens)

not_finish=0; // 當第一位的值達到 Queens 時結束

else

ai++; // 將 ail 的值取下一個值

} else if (ai == Queens)

ai=1;

else

ai++; // 將 ai 的值取下一個值

} else if (++i

4.2.4 Pascal 語言

以下列出尼克勞斯・維爾特的 Pascal 語言程式。此程式找出了八皇后問題的一個解。

program eightqueen1 (output);

var i : integer; q : boolean;

a : array 1 .. 8 of boolean;

b : array 2 .. 16 of boolean;

c : array -7 .. 7 of boolean;

x : array 1 .. 8 of integer;

procedure try (i : integer; var q : boolean);

var j : integer;

begin

j := 0;

repeat

j := j + 1;

q := false;

if a j and b i + j and c i - j then

begin

x i := j;

a j := false;

b i + j := false;

c i - j := false;

if i

4.2.5 Java 語言

使用回溯法進行求解八皇后問題 (Java 版本)，可直接複製到 N-Queens - LeetCode 測試。

class Solution {public List> solveNQueens (int n) {List> results = new ArrayList ;

// 使用 char 是為了展示結果時，直接使用 new String (char)。

// 一般情況下，使用 boolean 即可。

char result = new charnn;

for (int i = 0; i> results, char result, int x) {for (int j = 0; j = 0 && j>= 0; --i, --j) {if (resultij == 'Q') {return false;}

}

//...

//... ...... (x-1, y+1)

//... (x, y)

for (int i = x - 1, j = y + 1; i>= 0 && j > results, char result) {List list = new ArrayList (result.length);

for (char value : result) {list.add (new String (value));

}

results.add (list);

}

}

4.2.6 C++ 語言

include "iostream"
include "cmath"

using namespace std;

define Max 20 // 定義棋盤的最大值

int aMax;

int show (int S) // 定義出函式

{

	int i,p,q;

	int bMaxMax={0}; // 定義且初始化 b1 輸出模組

	for (i=1;i0)

	{if (knum) // 若滿足輸出陣列的要求就輸出該陣列

			{

				count++;

				printf ("% d:",count);

				show (num); // 呼叫輸出函式 show }

			k--; // 棋子位置不符合要求則退回前一步

			ak++; // 繼續尋找下一列位置

		}

	}

	printf ("總共有 % d \n",count,"個");

}

int main (void)

{

	int N,d;

	do

	{

		printf (" N 皇后問題的解 (N0&&N

大眾文化

在 1990 年代初期的著名電腦遊戲《第七訪客》中，伊格（Ego，玩家）在史陶夫的府邸的遊戲室裡碰到的象棋問題正是八個皇后問題。八皇后問題還在 NDS 平臺的著名電子遊戲《雷頓教授與不可思議的小鎮》中出現。