八皇后问题

八皇后问题是一个以国际象棋为背景的问题：如何能够在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。
八皇后问题在1990年代初期的著名电子游戏《第七访客》和NDS平台的著名电子游戏《雷顿教授与不可思议的小镇》中都有出现。
解题方法.
八个皇后在8x8棋盘上共有4,426,165,368（64C8）种摆放方法，但只有11×8 + 1×4 = 92个可行（皇后间互不攻击）的解。如果将旋转和对称的解归为一种的话，则一共有12个独立解，具体如下：
一个独立解（fundamental solution）通常有8个变体（含其最初形式），可以先对其进行旋转90°、180°和270°，加上其最初形式形成4个旋转变体，再针对一个固定位置比如纵轴进行反射（字母p的垂直反射是字母q），从而得到8个变体。但独立解10在旋转180°时与最初形式重合，所以只有4种变体即自身及其反射和90°旋转及其反射。
独立解11具有额外的性质。
独立解3的旋转90°再反射的变体，体现了阶梯状模式而被称为“楼梯解”，n皇后问题对n ≥ 4可通过特定公式得到楼梯解：
八皇后问题的12个独立解之间没有由数学性质决定出的次序，这里的独立解次序和所采用的变体是求独立解的特定搜索算法的输出结果（第一个解在棋盘左下角或左上角有一个皇后），不加以人为调整，比如：将独立解3的楼梯变体放在最前，将独立解10放在最后等等。
解的个数.
下表给出了n皇后问题的解的个数包括独立解U以及可行解D的个数：
可以注意到六皇后问题的解的个数比五皇后问题的解的个数要少。现在还没有已知公式可以对n计算n皇后问题的解的个数。
示例程序.
求独立解.
Python语言.
下面采用Python语言，生成八皇后问题的12个独立解：
<syntaxhighlight lang="python">
def queens(n):
queens(8)
</syntaxhighlight>
这里的算法核心环节是生成器委托codice_1。将这段代码保存入codice_2文件中，下面演示其执行结果：
<syntaxhighlight lang="console">
$ 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']
</syntaxhighlight>
jq语言.
下面采用纯函数式编程语言jq，生成八皇后问题的12个独立解：
<syntaxhighlight lang="python">
def queens(n):
queens(8)
</syntaxhighlight>
这里的算法核心环节是共递归运算codice_3。将这段代码保存入codice_4文件中，下面演示其执行结果：
<syntaxhighlight lang="console">
$ 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"]
</syntaxhighlight>
求可行解.
Icon语言.
下面采用Icon语言，生成八皇后问题的92个可行解：
<syntaxhighlight lang="icon">
global n
procedure main()
end
procedure pipeline(i)
end
procedure q(r)
end
procedure place(r, c)
end
</syntaxhighlight>
这里的算法核心环节是可逆赋值运算codice_5。将这段代码保存入codice_6文件中，下面演示其执行结果并提取其前两个解：
<syntaxhighlight lang="console">
$ 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
</syntaxhighlight>
C语言.
下面是求解n皇后的C代码，在程序中可以自己设置n个皇后以及选择是否打印出具体解。
<syntaxhighlight lang="C">
int A+ 1, B* 3 + 1, C* 3 + 1, k+ 1+ 1;
int inc, *a = A, *b = B + QUEENS, *c = C;
void lay(int i) {
int main(void) {
}</syntaxhighlight>
使用回溯法进行求解八皇后问题
<syntaxhighlight lang="C">
int queens(int Queens){
 int a[Queens+1]; //八皇后问题的皇后所在的行列位置，从1幵始算起，所以加1
 if(PRINTF_IN){
 printf((count-1)%3 ? " [%2d]:" : "\n[%2d]:", count);
 for(k=1; k<=Queens; k++) //输出结果
 return count;
int main()
 int Num ; 
 printf("输入一个N皇后数值：");
 scanf("%d" , &Num);
 printf("\n%d皇后有%d种配置\n",Num,queens(Num));
}</syntaxhighlight>
Pascal语言.
以下列出尼克劳斯·维尔特的Pascal语言程序。此程序找出了八皇后问题的一个解。
<syntaxhighlight lang="pascal">
program eightqueen1(output);
var i : integer; q : boolean;
procedure try( i : integer; var q : boolean);
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
writeln
end.</syntaxhighlight>
Java语言.
使用回溯法进行求解八皇后问题(Java版本)，可直接复制到N-Queens - LeetCode测试。
<syntaxhighlight lang="Java">
class Solution {
</syntaxhighlight>
C++语言.
<syntaxhighlight lang="C++">
using namespace std;
int a;
int show(int S) //定义出函数
 int i,p,q;
 int b //定义且初始化b[1[]输出模组 
 for(i=1;i<=S;i++) //按横列顺序输出a的座标 
 b[a=1;
 printf("(%d,%d)\t",i,a);
 printf("\n");
 for(p=1;p<=S;p++) //按棋盘的横列的顺序标明的位置
 for(q=1;q<=S;q++)
 if(b==1) //在第p行第q列放置一颗棋子 
 printf("x");
 else
 printf("o"); 
 printf("\n");
 return 0;
int check(int k) //定义check函数 
 int i;
 for(i=1;i<k;i++) 
 if((a|| (a[i-a(a[i-a==i-k) ) //检查是否有多颗棋子在同一个直行上
 return 0;
 return 1;
void check_m(int num) //定义函数 
 int k=1,count=0;
 printf("N皇后问题的所有解(包含经由旋转的解):\n");
 a=1;
 while(k>0)
 if(k<=num && a<=num) //从第k行第一列的位置开始，寻找之后的棋子的位置 
 if(check(k)==0) //第k行的a列不能放置棋子
 a //继续试探该前行的下一列：a[k+1 
 else
 k++; //第K行的位置已经确定完毕，继续寻找第k+1行棋子的位置
 a=1; //从第k+1的第一列开始查找
 else
 if(k>num) //若满足输出数组的要求就输出该数组 
 count++;
 printf("[%d]: ",count);
 show(num); //调用输出函数show()
 k--; //棋子位置不符合要求则退回前一步
 a++; //继续寻找下一列位置
 printf("总共有 %d \n",count,"个");
int main(void)
 int N,d;
 do
 printf(" N皇后问题的解(N<20) \n");
 printf("请输入N的值：_");
 scanf("%d",&N);
 printf("\n");
 if(N>0&&N<20)
 check_m(N); 
 break;
 else
 printf("输入错误，请重新输入");
 printf("\n\n");
 break; 
 while(1);
 system("pause");
 return 0;
</syntaxhighlight>