八皇后问题

八皇后问题是一个以国际象棋为背景的问题:如何能够在8×8的国际象棋棋盘上放置八个皇后,使得任何一个皇后都无法直接吃掉其他的皇后?为了达到此目的,任两个皇后都不能处于同一条横行、纵行或斜线上。八皇后问题可以推广为更一般的n皇后摆放问题:这时棋盘的大小变为n×n,而皇后个数也变成n当且仅当n = 1或n ≥ 4时问题有解[1]


abcdefgh
8
8
77
66
55
44
33
22
11
abcdefgh
八皇后问题的唯一对称解(不包括旋转和反射变换)

历史

八皇后问题最早是由西洋棋棋手马克斯·贝瑟尔(Max Bezzel)于1848年提出。第一个解在1850年由弗朗兹·诺克(Franz Nauck)给出。并且将其推广为更一般的n皇后摆放问题。诺克也是首先将问题推广到更一般的n皇后摆放问题的人之一。

在此之后,陆续有数学家对其进行研究,其中包括高斯康托,1874年,S.冈德尔提出了一个通过行列式来求解的方法[2],这个方法后来又被J.W.L.格莱舍加以改进。

1972年,艾兹格·迪杰斯特拉用这个问题为例来说明他所谓结构化编程的能力[3]。他对深度优先搜索回溯算法有着非常详尽的描述2

八皇后问题在1990年代初期的著名电子游戏第七访客NDS平台的著名电子游戏《雷顿教授与不可思议的小镇》中都有出现。

解题方法

八个皇后在8x8棋盘上共有4,426,165,368(64C8)种摆放方法,但只有92个互不相同的解。如果将旋转和对称的解归为一种的话,则一共有12个独立解,具体如下:


abcdefgh
8
8
77
66
55
44
33
22
11
abcdefgh
独立解1


abcdefgh
8
8
77
66
55
44
33
22
11
abcdefgh
独立解2


abcdefgh
8
8
77
66
55
44
33
22
11
abcdefgh
独立解3


abcdefgh
8
8
77
66
55
44
33
22
11
abcdefgh
独立解4


abcdefgh
8
8
77
66
55
44
33
22
11
abcdefgh
独立解5


abcdefgh
8
8
77
66
55
44
33
22
11
abcdefgh
独立解6


abcdefgh
8
8
77
66
55
44
33
22
11
abcdefgh
独立解7


abcdefgh
8
8
77
66
55
44
33
22
11
abcdefgh
独立解8


abcdefgh
8
8
77
66
55
44
33
22
11
abcdefgh
独立解9


abcdefgh
8
8
77
66
55
44
33
22
11
abcdefgh
独立解10


abcdefgh
8
8
77
66
55
44
33
22
11
abcdefgh
独立解11


abcdefgh
8
8
77
66
55
44
33
22
11
abcdefgh
独立解12

解的个数

下表给出了n皇后问题的解的个数包括独立解UOEIS中的数列A002562以及互不相同的解DOEIS中的数列A000170的个数:

n 1 2 3 4 5 6 7 8 9 10 11 12 13 14 ..
U 1 0 0 1 2 1 6 12 46 92 341 1,787 9,233 45,752 ..
D 1 0 0 2 10 4 40 92 352 724 2,680 14,200 73,712 365,596 ..

可以注意到六皇后问题的解的个数比五皇后问题的解的个数要少。现在还没有已知公式可以对n计算n皇后问题的解的个数。

示例程序

下面是求解n皇后的C代码,在程序中可以自己设置n个皇后以及选择是否打印出具体解。

#include <stdio.h>

#define QUEENS       8 /*皇后数量*/
#define IS_OUTPUT    1 /*(IS_OUTPUT=0 or 1),Output用于选择是否输出具体解,为1输出,为0不输出*/

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 <= 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 < 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;
}

以下列出尼克劳斯·维尔特Pascal语言程序[4]。此程序找出了八皇后问题的一个解。

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 < 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.

使用回溯法进行求解八皇后问题

#include<stdio.h>

#define PRINTF_IN 1 //定义是否打印,1:打印,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 && i<=Queens){  //处理尚未结束且还没处理到第Queens个元素
            for(flag=1,k=1; flag && k<i; k++) //判断是否有多个皇后在同一行
                if(a[k]==a[i])
                    flag=0;

            for (k=1; flag&&k<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>1 && a[i]==Queens)
                        a[i]=1;  //当a[i]为Queens时将a[i]的值置1
                    else
                        if(i==1 && 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<=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<=Queens; k++) //输出结果
                printf(" %d", a[k]); 
			}
   
            if(a[Queens-1]<Queens )
                a[Queens-1]++;  //修改倒数第二位的值
            else
                a[Queens-1]=1;

            i=Queens -1;    //开始寻找下一个满足条件的解
        }
    }
	return count;
}

int main()
{
	int Num ; 

	printf("输入一个N皇后数值:");
	scanf("%d" , &Num);
	printf("\n%d皇后有%d种配置\n",Num,queens(Num));
}

使用回溯法进行求解八皇后问题(Java版本)

public class Main {

    static char[][] board;
    static List<List<String>> res = new ArrayList<>();

    // find next valid position for Q by rows
    public static void backtrack(int i){
        if (i == board.length){
            res.add(construct(board));
            return;
        }

        // in each row, find valid positions
        for (int j = 1; j <= board.length - 1; j++){
            if (isValid(board, i, j)){
                board[i][j] = 'Q';
                backtrack(i+1); // by rows
                board[i][j] = '.';
            }
        }
    }

    // only need to check these cells: north or northwest or northeast of board[i][j]
    public static boolean isValid(char[][] board, int i, int j){
        for (int k = 1; k <= i-1; k++){
            if (board[i-k][j] == 'Q'){
                return false;
            }
        }
        for (int k = 1; k <= i-1 && k <= j-1 ; k++){
            if (board[i-k][j-k] == 'Q'){
                return false;
            }
        }
        for (int k = 1; k <= i-1 && k <= board.length-1-j; k++){
            if (board[i-k][j+k] == 'Q'){
                return false;
            }
        }
        return true;
    }

    public static List<String> construct(char[][] board){
        List<String> list = new ArrayList<>();

        for (int i = 1; i <= board.length-1; i++){
            StringBuilder sb = new StringBuilder();
            for (int j = 1; j <= board.length-1; j++){
                sb.append(board[i][j]);
            }
            list.add(sb.toString());
        }

        return list;
    }

    public static List<List<String>> solveNQueens(int n) {
        board = new char[n+1][n+1];
        for (int i  = 0; i < board.length; i++){
            for (int j = 0; j < board.length; j++){
                board[i][j] = '.';
            }
        }

        backtrack(1);

        return res;
    }


    public static void main(String[] args){
        int n = 4;

        solveNQueens(n);

        System.out.println("when n=" + n + ", there are " + res.size() + " solutions:\n");
        for (List<String> list : res){
            for (String row : list){
                for (int i = 0; i < row.length(); i++){
                    System.out.print(row.charAt(i) + " ");
                }
                System.out.println();
            }
            System.out.println();
        }
    }
}

參考資料

  1. Watkins, John J. (2004). Across the Board: The Mathematics of Chess Problems. Princeton: Princeton University Press. ISBN 0-691-11503-6
  2. W. W. Rouse Ball (1960) The Eight Queens Problem, in Mathematical Recreations and Essays, Macmillan, New York, pp 165-171.
  3. 奧利-約翰·達爾, 艾兹赫尔·戴克斯特拉, 東尼·霍爾 Structured Programming, Academic Press, London, 1972 ISBN 0-12-200550-3 see pp 72-82 for Dijkstra's solution of the 8 Queens problem.
  4. Wirth, 1976, p. 145

[外部链接] [补充阅读] General method n queens with implementation in java 页面存档备份,存于

This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.