八皇后编程实现

八皇后问题说明

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于 1848 年
提出:在 8×8 格的国际象棋上摆放八个皇后,使其不能互相攻击,即:任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法

回溯算法思路

八皇后问题如果用穷举法需要尝试88=16,777,216种情况。每一列放一个皇后,可以放在第 1 行,第 2 行,……,直到第8行。穷举的时候从所有皇后都放在第1行的方案开始,检验皇后之间是否会相互攻击。如果会,把列H的皇后挪一格,验证下一个方案。移到底了就“进位”到列G的皇后挪一格,列H的皇后重新试过全部的8行。这种方法是非常低效率的,因为它并不是哪里有冲突就调整哪里,而是盲目地按既定顺序枚举所有的可能方案。
回溯算法优于穷举法。将列A的皇后放在第一行以后,列B的皇后放在第一行已经发生冲突。这时候不必继续放列C的皇后,而是调整列B的皇后到第二行,继续冲突放第三行,不冲突了才开始进入列C。如此可依次放下列A至E的皇后。将每个皇后往右边横向、斜向攻击的点位用叉标记,发现列F的皇后无处安身。这时回溯到列E的皇后,将其位置由第4行调整为第8行,进入列F,发现皇后依然无处安身,再次回溯列E。此时列E已经枚举完所有情况,回溯至列D,将其由第2行移至第7行,再进入列E继续。按此算法流程最终找到如图3所示的解,成功在棋盘里放下了8个“和平共处”的皇后。继续找完全部的解共92个。
回溯算法求解八皇后问题的原则是:有冲突解决冲突,没有冲突往前走,无路可走往回退,走到最后是答案。为了加快有无冲突的判断速度,可以给每行和两个方向的每条对角线是否有皇后占据建立标志数组。放下一个新皇后做标志,回溯时挪动一个旧皇后清除标志。

在这里插入图片描述

编程实现

Haskall

queens :: Int -> [[Int]]
queens n = map reverse $ queens' n
    where queens' 0       = [[]]
          queens' k       = [q:qs | qs <- queens' (k-1), q <- [1..n], isSafe q qs]
          isSafe   try qs = not (try `elem` qs || sameDiag try qs)
          sameDiag try qs = any (\(colDist,q) -> abs (try - q) == colDist) $ zip [1..] qs

GO

package main
import "fmt"
func main() { 
    var balance = [8]int{0, 0, 0, 0, 0, 0, 0, 0} 
    queen(balance, 0)
}
func queen(a [8]int, cur int) {
    if cur == len(a) {
        fmt.Print(a)  
        fmt.Println()  
        return
    } 
    for i := 0; i < len(a); i++ {  
        a[cur] = i  
        flag := true  
        for j := 0; j < cur; j++ {   
            ab := i - a[j]   
            temp := 0   
            if ab > 0 {
                temp = ab   
            } else {    
                temp = -ab
            }
            if a[j] == i || temp == cur-j {
                flag = false    
                break  
            }
         }  
         if flag {   
             queen(a, cur+1)  
         }
    }
}

JS

function queen(a, cur) {
    if (cur==a.length) { console.log(a); return; }
    for (var i = 0; i < a.length; i++) {
        a[cur] = i;
        var flag = true;
        for (var j = 0; j < cur; j++) {
            var ab = i - a[j];
            if (a[j]==i||(ab>0?ab:-ab)==cur-j) { flag=false; break };
        };
        if (flag) { queen(a,cur+1); }
    };
};
queen([1,1,1,1,1,1,1,1],0);

C++

#include <iostream>
using namespace std;
 
const int N = 8;
int arr[10], total_cnt;
// arr记录每一行(X)皇后的Y坐标
 
bool isPlaceOK(int *a, int n, int c) {
    for (int i = 1; i <= n - 1; ++i) {
        if (a[i] == c || a[i] - i == c - n || a[i] + i == c + n)
            return false;
        //检查位置是否可以放
        //c是将要放置的位置
        //a[i] == c如果放在同一列,false
        //a[i] -+ i = c -+ n 如果在对角线上,false
    }
    return true;
}
 
void printSol(int *a) {
    for (int i = 1; i <= N; ++i) { //遍历每一行
        for (int j = 1; j <= N; ++j) { //遍历每一列
            cout << (a[i] == j ? "X" : "-") << " ";;
        } //如果标记数组中这一行的皇后放在j位置,则输出X,否则输出-,
        //用空格分隔
        cout << endl; //每一行输出一个换行
    }
    cout << endl; //每一组数据一个换行分隔
}
 
void addQueen(int *a, int n) {
    if (n > N) { //n代表从第一行开始放置
        printSol(a);
        total_cnt++;
        return ;
    }
    for (int i = 1; i <= N; ++i) { //i从第1列到第N列遍历
        if (isPlaceOK(a, n, i)) {
            a[n] = i; //如果可以放置,就把皇后放在第n行第i列
            addQueen(a, n + 1);
        }
    }
 
}
 
int main() {
    addQueen(arr, 1);
    cout << "total: " << total_cnt << " solutions.\n";
    return 0;
}

Pascal

program queen; var a:array[1…8]of longint; //记录皇后的行坐标
b,c,d:array[-7…16]of longint; //行,右上,右下斜线的占位标志 m,ans:longint;
procedure queen(j:longint); var i:longint; begin
if j>8 then
begin
inc(ans); //满足条件,找到一种方案
exit;
end;
for i:=1 to 8 do//每个皇后位置有八种可能
if(b[i]=0)and(c[i+j]=0)and(d[j-i]=0)then //如果位置没有被占则运行
begin
a[j]:=i; //皇后放置在此行
b[i]:=1; //占领第i行
c[i+j]:=1; //占领右上
d[j-i]:=1; //占领右下
queen(j+1); //递归
b[i]:=0; //回溯,恢复行占位标志
c[i+j]:=0; //回溯,恢复斜上方(右上)占位标志
d[j-i]:=0; ///回溯,恢复斜下方(右下)占位标志
end; end; begin //主程序
for m:=-7 to 16 do //数据初始化为0
begin
b[m]:=0; //行数据初始化为0
c[m]:=0; //右上数据初始化为0
d[m]:=0; //右下数据初始化为0
end;
ans:=0;
queen(1); //开始放置第一个皇后
writeln(ans); end.

Java

public class Queen {
    private int[] column; //同栏是否有皇后,1表示有
    private int[] rup; //右上至左下是否有皇后
    private int[] lup; //左上至右下是否有皇后
    private int[] queen; //解答
    private int num; //解答编号
 
    public Queen() {
        column = new int[8+1];
        rup = new int[(2*8)+1];
        lup = new int[(2*8)+1];
        for (int i = 1; i <= 8; i++)
            column[i] = 0;
        for (int i = 1; i <= (2*8); i++)
            rup[i] = lup[i] = 0;  //初始定义全部无皇后
        queen = new int[8+1];
    }
 
    public void backtrack(int i) {
        if (i > 8) {
            showAnswer();
        } else {
            for (int j = 1; j <= 8; j++) {
                if ((column[j] == 0) && (rup[i+j] == 0) && (lup[i-j+8] == 0)) {
                    //若无皇后
                    queen[i] = j; //设定为占用
                    column[j] = rup[i+j] = lup[i-j+8] = 1;
                    backtrack(i+1);  //循环调用
                    column[j] = rup[i+j] = lup[i-j+8] = 0;
                }
            }
        }
    }
 
    protected void showAnswer() {
        num++;
        System.out.println("\n解答" + num);
        for (int y = 1; y <= 8; y++) {
            for (int x = 1; x <= 8; x++) {
                if(queen[y]==x) {
                    System.out.print("Q");
                } else {
                    System.out.print(".");
                }
            }
            System.out.println();
        }
    }
 
    public static void main(String[] args) {
        Queen queen = new Queen();
        queen.backtrack(1);
    }
}

Erlang

-module(queen).
-export([printf/0, attack_range/2]).
-define(MaxQueen, 4).%寻找字符串所有可能的排列
%perms([]) ->% [[]];
%perms(L) ->% [[H | T] || H <- L, T <- perms(L -- [H])].
perms([]) ->[[]];
perms(L) ->[[H | T] || H <- L, T <- perms(L -- [H]), attack_range(H,T) == []].printf() ->L = lists:seq(1, ?MaxQueen),io:format("~p~n", [?MaxQueen]),perms(L).
%检测出第一行的数字攻击到之后各行哪些数字%left向下行的左侧检测%right向下行的右侧检测
attack_range(Queen, List) ->attack_range(Queen, left, List) ++ attack_range(Queen, right, List).attack_range(_, _, []) ->[];
attack_range(Queen, left, [H | _]) when Queen - 1 =:= H ->[H];
attack_range(Queen, right, [H | _]) when Queen + 1 =:= H ->[H];
 
attack_range(Queen, left, [_ | T]) ->attack_range(Queen - 1, left, T);attack_range(Queen, right, [_ | T]) ->attack_range(Queen + 1, right, T).

python

def queen(A, cur=0):
    if cur == len(A):
        print(A)
        return 0
    for col in range(len(A)):  # 遍历当前行的所有位置
        A[cur] = col
        for row in range(cur):  # 检查当前位置是否相克
            if A[row] == col or abs(col - A[row]) == cur - row:
                break
        else:  # 如果完成了整个遍历,则说明位置没有相克
            queen(A, cur+1)  # 计算下一行的位置
queen([None]*8)

C#

using System;
using System.Collections.Generic;
namespace EightQueens_CSharp {
    public class EightQueens {
        private List<int[]> solutions;
        public List<int[]> GetSolutions(int queenCount) {
            solutions = new List<int[]>();
            List<int> queenList = new List<int>();
            for (int i = 0; i < queenCount; i++) {
                queenList.Add(0);
            }
            putQueen(queenCount, queenList, 0);
            printSolutions(solutions);
            return solutions;
        }
         
        private void putQueen(int queenCount, List<int> queenList, int nextY) {
            for (queenList[nextY] = 0; queenList[nextY] < queenCount; queenList[nextY]++) {
                if (checkConflict(queenList, nextY) == false) {
                    nextY++;
                    if (nextY < queenCount) {
                        putQueen(queenCount, queenList, nextY);
                    }
                    else {
                        solutions.Add(queenList.ToArray());
                        Console.WriteLine(string.Join(", ", queenList));
                    }
                    nextY--;
                }
            }
        }
         
        private bool checkConflict(List<int> queenList, int nextY) {
            for (int positionY = 0; positionY < nextY; positionY++) {
                if (Math.Abs(queenList[positionY] - queenList[nextY]) == Math.Abs(positionY - nextY) || queenList[positionY] == queenList[nextY]) {
                    return true;
                }
            }
            return false;
        }
         
        private void printSolutions(List<int[]> solutions) {
            int count = 0;
            foreach (var solution in solutions) {
                Console.WriteLine("Solution: {0}", count++);
                int queenCount = solution.Length;
                for (int i = 0; i < queenCount; i++) {
                    printLine(solution[i], queenCount);
                }
                Console.WriteLine("------------------");
            }
        }
         
        private void printLine(int pos, int width) {
            for (int i = 0; i < width; i++) {
                if (pos == i) {
                    Console.Write(" x");
                }
                else {
                    Console.Write(" .");
                }
            }
            Console.WriteLine();
        }
    }
}

Scheme

#lang racket (define (enumerate-interval low high) (cond ((> low high) null)
((= low high) (list high))
(else (cons low (enumerate-interval (+ 1 low) high)))))
(define (flatmap proc seq) (foldr append null (map proc seq)))
(define empty-board null)
(define (safe? test-column positions) (define (two-coordinate-safe? coordinate1 coordinate2)
(let ((row1 (row coordinate1))
(row2 (row coordinate2))
(col1 (column coordinate1))
(col2 (column coordinate2)))
(if (or (= row1 row2)
(= (abs (- row1 row2)) (abs (- col1 col2))))
#f
#t))) (let ((test-coordinate (get-coordinate-by-column test-column positions)))
(foldr (lambda (coordinate results)
(and (two-coordinate-safe? test-coordinate coordinate)
results))
#t
(remove test-coordinate positions))))

     (define (adjoin-position new-row new-column existing-positions)     (cons (make-coordinate new-row new-column)

existing-positions))

   (define (make-coordinate row column)     (list row column))   (define (row coordinate)     (car coordinate))   (define (column

coordinate) (cadr coordinate)) (define (get-coordinate-by-column
target-column coordinates) (cond ((null? coordinates) null)
((= target-column (column (car coordinates))) (car coordinates))
(else (get-coordinate-by-column target-column (cdr coordinates)))))

 (define (queens board-size)     (define (queen-cols k) 
 (if (= k 0) 
     (list empty-board) 
     (filter 
      (lambda (positions) (safe? k positions)) 
      (flatmap 
       (lambda (rest-of-queens) 
         (map (lambda (new-row) 
                (adjoin-position 
                 new-row k rest-of-queens)) 
              (enumerate-interval 1 board-size))) 
       (queen-cols (- k 1))))))     (queen-cols board-size))   ;敲(queens 8),得到运行结果,每个坐标,第一个代表行,第二个代表列 ;纯递归思路,运行环境racket 7.5

Lua

 local num = 0;
    local t = socket.gettime();
    local function searchQueen(tab)
        local data = clone(tab)
        if data[2] == #data[1] then
            num = num + 1
            -- return print("result..", num, "  ==>  ", table.concat(data[1], ","))
            return
        end
        data[2] = data[2] + 1;
        for i = 1, 8 do
            data[1][data[2]] = i
            local bOk = true
            for j = 1, data[2]-1 do
                if (data[1][j] == i) or (math.abs(i - data[1][j]) == data[2] - j) then
                    bOk = false
                    break
                end
            end
            if bOk then
                searchQueen(data)
            end
        end
    end
    searchQueen({{0,0,0,0,0,0,0,0}, 0})
    print("EIGHT QUEEN result == ", num, "\ntime cost == ", socket.gettime() - t)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值