八皇后问题

八皇后问题是一个以国际象棋为背景的问题：如何能够在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可通过特定公式得到楼梯解：
八皇后问题的12个独立解之间没有由数学性质决定出的次序，这里的独立解次序和所采用的变体是求独立解的特定搜索算法的输出结果（第一个解在棋盘左下角或左上角有一个皇后），不加以人为调整，比如：将独立解3的楼梯变体放在最前，将独立解10放在最后等等。
解的个数.
下表给出了n皇后问题的解的个数包括独立解U以及可行解D的个数：
可以注意到六皇后问题的解的个数比五皇后问题的解的个数要少。现在还没有已知公式可以对n计算n皇后问题的解的个数。
示例程序.
求独立解.
Python语言.
下面采用Python语言在致力保持不可变性原则下，生成八皇后问题的12个独立解：
def queens(n):
 def q(pl, r):
 def place(c): 
 return r+c not in pl[1] and r-c not in pl[2]
 return ((pl[0]+[c], pl[1]|{r+c}, pl[2]|{r-c}, pl[3]-{c})
 for c in pl[3] if place(c))
 def pipeline(pl, i):
 for ipl in q(pl, i):
 if i+1 &lt; n:
 yield from pipeline(ipl, i+1) 
 else:
 yield ipl[0]
 def toletter(x): 
 return 'abcdefghijklmnopqrstuvwxyz'[x]
 def fund_solut(fl):
 def inversed(xl):
 return (xl.index(i) for i in range(0, n))
 def variants(xl):
 rl = [xl]
 rl += [[*inversed(x)] for x in rl]
 rl += [[*reversed(x)] for x in rl]
 rl += [[n-1-i for i in x] for x in rl]
 return (".join(toletter(i) for i in x) for x in rl)
 rs = set()
 for i in fl:
 if rs.isdisjoint(ks):
 rs |= ks
 yield i
 for i in fund_solut(
 pipeline(([], set(), set(), {*range(0, n)}), 0)):
 rl = sorted(toletter(v)+str(k+1) for k, v in enumerate(i))
 print(rl)
queens(8)
这里的算法核心环节是[[生成器 (计算机编程)|生成器]][[委托 (编程)|委托]]codice_1。将这段代码保存入codice_2文件中，下面演示其执行结果：
$ 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']
jq语言.
下面采用[[纯函数式编程|纯函数式编程语言]][[jq语言|jq]]，生成八皇后问题的12个独立解：
def queens(n):
 def q: . as $pl 
 | $pl[4] as $r 
 | $pl[3] as $cl | $cl[] | . as $c
 | ($r+$c | tostring) as $k0
 | ($r-$c | tostring) as $k1
 | def place: 
 ($k0 | in($pl[1]) | not)
 and ($k1 | in($pl[2]) | not); 
 select(place)
 | [$pl[0]+[$c], $pl[1]+{$k0:null},
 $pl[2]+{$k1:null}, $cl-[$c], $r+1];
 def pipeline(n): 
 q | if n &gt; 1 then pipeline(n-1) end;
 def toletter: 
 "abcdefghijklmnopqrstuvwxyz"[.:.+1];
 def fund_solut(f):
 def inverse: . as $xl
 | reduce range(0; n) as $i
 ([]; .+[$xl | index($i)]);
 def variants:
 [., inverse] | map(., reverse)
 | map(., map(n-1-.)) 
 | map(map(toletter) | add);
 foreach f as $i 
 ([null, {}]; .[1] as $ml
 | ($i | 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]
 | pipeline(n) | .[0]) 
 | map(toletter) | to_entries
 | map(.value+(.key+1 | tostring)) | sort;
queens(8)
这里的算法核心环节是[[管道 (软件)|管道]]机制。将这段代码保存入codice_3文件中，下面演示其执行结果：
$ 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"]
求可行解.
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, s[x[i]] || i)
 u := sort(r)
 v := get(u)
 every v ||:= "," || !u
 write(v)
end
procedure pipeline(i)
 if i &lt; n then
 suspend [q(i)] ||| pipeline(i+1)
 else 
 suspend [q(i)]
end
procedure q(r)
 suspend place(r, 1 to n)
end
procedure place(r, c)
 static col, up, down
 initial {
 col := list(n, 0) 
 down := list(n*2-1, 0)
 up := list(n*2-1, 0) 
 if col[c] = 0 = down[r+c-1] = up[r-c+n] then
 suspend col[c] &lt;- down[r+c-1] &lt;- up[r-c+n] &lt;- c
end
这里的算法核心环节是可逆[[赋值语句|赋值运算]]codice_4。将这段代码保存入codice_5文件中，下面演示其执行结果并提取其codice_6个解中的前两个解：
$ 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
Python语言.
下面采用[[Python]]语言基于共享变量，生成八皇后问题的92个可行解：
def queens(n):
 col = [False]*n
 down = [False]*(n*2-1)
 up = [False]*(n*2-1)
 def q(r):
 def place(c): 
 return col[c] == False == down[r+c] == up[r-c]
 for c in range(0, n):
 if place(c):
 col[c] = down[r+c] = up[r-c] = True
 yield [c]
 col[c] = down[r+c] = up[r-c] = False
 def pipeline(i):
 for r in q(i):
 if i+1 &lt; n:
 for s in pipeline(i+1):
 yield r + s
 else:
 yield r
 def toletter(x): 
 return 'abcdefghijklmnopqrstuvwxyz'[x]
 for i in (pipeline(0)):
 rl = sorted(toletter(v)+str(k+1)
 for k, v in enumerate(i))
 print(rl)
queens(8)
这里的算法核心环节是在[[生成器 (计算机编程)|生成器]]之间共享[[静态变量]]。将这段代码保存入codice_2文件中，下面演示其执行结果并提取其codice_6个解中的前两个解：
$ 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个横行上执行纵列选择函数codice_9的次数分别为：codice_10，共计codice_11次，codice_9在每个横行上针对纵列执行函数codice_13的次数都是codice_14，每横行的选择数分别为：codice_15，总计codice_16次，其中每横行的成功存乎最终结果者之数分别为：codice_17，总计codice_18个。
可以进一步将其写为下面的Python代码，其执行方式和结果同于前述：
def queens(n):
 cs = [0]*n
 cl = [*range(0, n)]
 down = [False]*(n*2-1)
 up = [False]*(n*2-1)
 def q(r):
 def place(c): 
 return down[r+c] == False == up[r-c]
 for i, c in enumerate(cl):
 if place(c):
 cs[r] = c
 del cl[i]
 down[r+c] = up[r-c] = True
 yield None
 cl.insert(i, c)
 down[r+c] = up[r-c] = False
 def pipeline(i):
 for _ in q(i):
 if i+1 &lt; n:
 yield from pipeline(i+1)
 else:
 yield cs
 def toletter(x): 
 return 'abcdefghijklmnopqrstuvwxyz'[x]
 for i in (pipeline(0)):
 rl = sorted(toletter(v)+str(k+1)
 for k, v in enumerate(i))
 print(rl)
queens(8)
这个算法在8个横行上执行纵列选择函数codice_9的次数分别为：codice_10，共计codice_11次，codice_9在8个横行上针对纵列执行函数codice_13的次数分别为：codice_24，每横行的选择数分别为：codice_25，总计codice_26次，其中每横行的成功存乎最终结果者之数分别为：codice_17，总计codice_18个。
下面将其写为[[过程式编程|过程式]]Python代码，其执行方式和结果同于前述：
def queens(n):
 cs = [0]*n
 cl = [*range(0, n)]
 down = [False]*(n*2-1)
 up = [False]*(n*2-1)
 rl = ["]*n
 toletter = lambda x: \
 'abcdefghijklmnopqrstuvwxyz'[x]
 def q(r):
 if r &lt; n:
 for i, c in enumerate(cl):
 if down[r+c] == False == up[r-c]:
 cs[r] = c
 del cl[i]
 down[r+c] = up[r-c] = True
 q(r+1)
 cl.insert(i, c)
 down[r+c] = up[r-c] = False
 else:
 for k, v in enumerate(cs):
 rl[k] = toletter(v)+str(k+1)
 print(sorted(rl))
 q(0) 
queens(8)
最后将其写为[[指令式编程|指令式]]Python代码，其执行方式和结果同于前述：
def queens(n):
 cs = [0]*n
 ps = [0]*n
 cl = [*range(0, n)]
 down = [False]*(n*2-1)
 up = [False]*(n*2-1)
 rl = ["]*n
 toletter = lambda x: \
 'abcdefghijklmnopqrstuvwxyz'[x]
 r = 0
 while r &gt;= 0:
 p = ps[r]
 if p != 0:
 c = cs[r]
 cl.insert(p-1, c)
 down[r+c] = up[r-c] = False
 for i, c in enumerate(cl[p:]):
 if down[r+c] == False == up[r-c]:
 cs[r] = c
 ps[r] = p+i+1
 break
 if p != ps[r]:
 del cl[p+i]
 down[r+c] = up[r-c] = True
 r += 1
 else:
 ps[r] = 0
 r -= 1
 if r == n:
 for k, v in enumerate(cs):
 rl[k] = toletter(v)+str(k+1)
 print(sorted(rl))
 r -= 1
queens(8)
C语言.
下面是求解n皇后的[[C语言|C]]代码，在程序中可以自己设置n个皇后以及选择是否打印出具体解。
int A[QUEENS + 1], B[QUEENS * 3 + 1], C[QUEENS * 3 + 1], k[QUEENS + 1][QUEENS + 1];
int inc, *a = A, *b = B + QUEENS, *c = C;
void lay(int i) 
 int j = 0, t, u;
 while (++j &lt;= QUEENS)
 if (a[j] + b[j - i] + c[j + i] == 0) {
 k[i][j] = a[j] = b[j - i] = c[j + i] = 1;
 if (i &lt; QUEENS) lay(i + 1);
 else {
 ++inc;
 if (IS_OUTPUT) {
 for (printf("(%d)\n", inc), u = QUEENS + 1; --u; printf("\n"))
 for (t = QUEENS + 1; --t; ) k[t][u] ? printf("Q ") : printf("+ ");
 printf("\n\n\n");
 a[j] = b[j - i] = c[j + i] = k[i][j] = 0;
int main(void)
 lay(1);
 printf("%d皇后共计%d个解\n", QUEENS, inc);
 return 0;
使用回溯法进行求解八皇后问题
int queens(int Queens)
 int i, k, flag, not_finish=1, count=0;
 //正在处理的元素下标，表示前i-1个元素已符合要求，正在处理第i个元素
 int a[Queens+1]; //八皇后问题的皇后所在的行列位置，从1幵始算起，所以加1
 i=1;
 a[1]=1; //为数组的第一个元素赋初值
 printf("%d皇后的可能配置是:",Queens);
 while(not_finish){ //not_finish=l:处理尚未结束
 while(not_finish &amp;&amp; i&lt;=Queens){ //处理尚未结束且还没处理到第Queens个元素
 for(flag=1,k=1; flag &amp;&amp; k&lt;i; k++) //判断是否有多个皇后在同一行
 if(a[k]==a[i])
 flag=0;
 for (k=1; flag&amp;&amp;k&lt;i; k++) //判断是否有多个皇后在同一对角线
 if( (a[i]==a[k]-(k-i)) || (a[i]==a[k]+(k-i)) )
 flag=0;
 if(!flag){ //若存在矛盾不满足要求，需要重新设置第i个元素
 if(a[i]==a[i-1]){ //若a[i]的值已经经过一圈追上a[i-1]的值
 i--; //退回一步，重新试探处理前一个元素
 if(i&gt;1 &amp;&amp; a[i]==Queens)
 a[i]=1; //当a[i]为Queens时将a[i]的值置1
 else
 if(i==1 &amp;&amp; a[i]==Queens)
 not_finish=0; //当第一位的值达到Queens时结束
 else
 a[i]++; //将a[il的值取下一个值
 }else if(a[i] == Queens)
 a[i]=1;
 else
 a[i]++; //将a[i]的值取下一个值
 }else if(++i&lt;=Queens)
 if(a[i-1] == Queens )
 a[i]=1; //若前一个元素的值为Queens则a[i]=l
 else
 a[i] = a[i-1]+1; //否则元素的值为前一个元素的下一个值
 if(not_finish){
 ++count;
 if(PRINTF_IN){
 printf((count-1)%3 ? " [%2d]:" : "\n[%2d]:", count);
 for(k=1; k&lt;=Queens; k++) //输出结果
 printf(" %d", a[k]); 
 
 if(a[Queens-1]&lt;Queens )
 a[Queens-1]++; //修改倒数第二位的值
 else
 a[Queens-1]=1;
 i=Queens -1; //开始寻找下一个满足条件的解
 return count;
int main()
 int Num ; 
 printf("输入一个N皇后数值：");
 scanf("%d" , &amp;Num);
 printf("\n%d皇后有%d种配置\n",Num,queens(Num));
Pascal语言.
以下列出[[尼克劳斯·维尔特]]的[[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 &lt; 8 then
 begin
 try( i + 1, q);
 if not q then
 begin 
 a[ j] := true; 
 b[ i + j] := true; 
 c[ i - j] := true;
 end
 end 
 else 
 q := true
 end
 until q or (j = 8);
 end;
begin
for i := 1 to 8 do a[ i] := true;
for i := 2 to 16 do b[ i] := true;
for i := -7 to 7 do c[ i] := true;
try( 1, q);
if q then
 for i := 1 to 8 do write( x[ i]:4);
writeln
end.
Java语言.
使用回溯法进行求解八皇后问题(Java版本)，可直接复制到N-Queens - LeetCode测试。
class Solution {
 public List&lt;List&lt;String» solveNQueens(int n) {
 List&lt;List&lt;String» results = new ArrayList&lt;&gt;();
 // 使用 char[][] 是为了展示结果时，直接使用 new String(char[])。
 // 一般情况下，使用 boolean[][] 即可。
 char[][] result = new char[n][n];
 for (int i = 0; i &lt; n; ++i) {
 for (int j = 0; j &lt; n; ++j) {
 result[i][j] = '.';
 backtrack(results, result, 0);
 return results;
 private static void backtrack(List&lt;List&lt;String» results, char[][] result, int x) {
 for (int j = 0; j &lt; result.length; ++j) {
 if (isValid(result, x, j)) {
 result[x][j] = 'Q';
 if (x == result.length - 1) {
 showResult(results, result);
 // 可以直接 break
 } else {
 // 皇后问题中，不会出现一行出现多个，所以直接跳到下一行
 backtrack(results, result, x + 1);
 result[x][j] = '.';
 private static boolean isValid(char[][] result, int x, int y) {
 // ... (0, y)
 // ... (x-1, y)
 // ... (x, y)
 for (int i = 0; i &lt; x; ++i) {
 if (result[i][y] == 'Q') {
 return false;
 // ... (x-1, y-1)
 // ... ... (x, y)
 for (int i = x - 1, j = y - 1; i &gt;= 0 &amp;&amp; j &gt;= 0; --i, --j) {
 if (result[i][j] == 'Q') {
 return false;
 // ... ... (x-1, y+1)
 // ... (x, y)
 for (int i = x - 1, j = y + 1; i &gt;= 0 &amp;&amp; j &lt; result.length; --i, ++j) {
 if (result[i][j] == 'Q') {
 return false;
 return true;
 private static void showResult(List&lt;List&lt;String» results, char[][] result) {
 List&lt;String&gt; list = new ArrayList&lt;&gt;(result.length);
 for (char[] value : result) {
 list.add(new String(value));
 results.add(list);
C++语言.
using namespace std;
int a[Max];
int show(int S) //定义出函数
 int i,p,q;
 int b[Max][Max]={0}; //定义且初始化b[1][]输出模组 
 for(i=1;i&lt;=S;i++) //按横列顺序输出a[i]的座标 
 b[i][a[i]]=1;
 printf("(%d,%d)\t",i,a[i]);
 printf("\n");
 for(p=1;p&lt;=S;p++) //按棋盘的横列的顺序标明的位置
 for(q=1;q&lt;=S;q++)
 if(b[p][q]==1) //在第p行第q列放置一颗棋子 
 printf("x");
 else
 printf("o"); 
 printf("\n");
 return 0;
 
int check(int k) //定义check函数 
 int i;
 for(i=1;i&lt;k;i++) 
 if((a[i]==a[k]) || (a[i]-a[k]==k-i)|| (a[i]-a[k]==i-k) ) //检查是否有多颗棋子在同一个直行上
 return 0;
 return 1;
 
void check_m(int num) //定义函数 
 int k=1,count=0;
 printf("N皇后问题的所有解(包含经由旋转的解):\n");
 a[k]=1;
 while(k&gt;0)
 if(k&lt;=num &amp;&amp; a[k]&lt;=num) //从第k行第一列的位置开始，寻找之后的棋子的位置 
 if(check(k)==0) //第k行的a[k]列不能放置棋子
 a[k]++; //继续试探该前行的下一列：a[k+1] 
 else
 k++; //第K行的位置已经确定完毕，继续寻找第k+1行棋子的位置
 a[k]=1; //从第k+1的第一列开始查找
 else
 if(k&gt;num) //若满足输出数组的要求就输出该数组 
 count++;
 printf("[%d]: ",count);
 show(num); //调用输出函数show()
 k--; //棋子位置不符合要求则退回前一步
 a[k]++; //继续寻找下一列位置
 printf("总共有 %d \n",count,"个");
 
int main(void)
 int N,d;
 do
 printf(" N皇后问题的解(N&lt;20) \n");
 printf("请输入N的值：_");
 scanf("%d",&amp;N);
 printf("\n");
 if(N&gt;0&amp;&amp;N&lt;20)
 check_m(N); 
 break;
 else
 printf("输入错误，请重新输入");
 printf("\n\n");
 break; 
 while(1);
 system("pause");
 return 0;
大众文化.
在1990年代初期的著名电脑游戏《[[第七访客]]》中，伊格（Ego，玩家）在史陶夫的[[宅第|府邸]]的游戏室里碰到的象棋问题正是八个皇后问题。八皇后问题还在[[任天堂DS|NDS]]平台的著名电子游戏《[[雷顿教授与不可思议的小镇]]》中出现。
外部链接.
[[Category:数学西洋棋问题]]
[[Category:组合数学]]
[[Category:组合计数]]