【小伟哥游戏之路】Python之八皇后问题的算法实例

今天是2019年10月24日 程序员节日

正好在看到python资料中 出现了八皇后的问题

八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。计算机发明后,有多种计算机语言可以解决此问题。

 

PYTHON 实现

#* queen problem with recurison
BOARD_SIZE = 8

def under_attack(col, queens):
   left = right = col
   for r, c in reversed(queens):
 #左右有冲突的位置的列号
       left, right = left - 1, right + 1

       if c in (left, col, right):
           return True
   return False

def solve(n):
   if n == 0:
       return [[]]

   smaller_solutions = solve(n - 1)

   return [solution+[(n,i+1)]
       for i in xrange(BOARD_SIZE)
           for solution in smaller_solutions
               if not under_attack(i+1, solution)]
for answer in solve(BOARD_SIZE):
   print answer

 

 

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;
static int gEightQueen[8] = { 0 }, gCount = 0;
void print()//输出每一种情况下棋盘中皇后的摆放情况
{
    for (int i = 0; i < 8; i++)
    {   
        int inner;
        for (inner = 0; inner < gEightQueen[i]; inner++)
            cout << "0";
            cout <<"#";
        for (inner = gEightQueen[i] + 1; inner < 8; inner++)
            cout << "0";
        cout << endl;
    }
    cout << "==========================\n";
}
int check_pos_valid(int loop, int value)//检查是否存在有多个皇后在同一行/列/对角线的情况
{
    int index;
    int data;
    for (index = 0; index < loop; index++)
    {
        data = gEightQueen[index];
        if (value == data)
            return 0;
        if ((index + data) == (loop + value))
            return 0;
        if ((index - data) == (loop - value))
            return 0;
    }
    return 1;
}
void eight_queen(int index)
{
    int loop;
    for (loop = 0; loop < 8; loop++)
    {
        if (check_pos_valid(index, loop))
        {
            gEightQueen[index] = loop;
            if (7 == index)
            {
                gCount++, print();
                gEightQueen[index] = 0;
                return;
            }
            eight_queen(index + 1);
            gEightQueen[index] = 0;
        }
    }
}
int main(int argc, char*argv[])
{
    eight_queen(0);
    cout << "total=" << gCount << endl;
    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).


 

八皇后问题是一个古老而著名的问题,是回溯算法的典型例题。该问题是十九世纪著名的数学家高斯1850年提出:在8X8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。   高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。   对于八皇后问题的实现,如果结合动态的图形演示,则可以使算法的描述更形象、更生动,使教学能产生良好的效果。下面是用Turbo C实现的八皇后问题的图形程序,能够演示全部的92组解。八皇后问题动态图形的实现,主要应解决以下两个问题。   (1)回溯算法的实现   (a)为解决这个问题,我们把棋盘的横坐标定为i,纵坐标定为j,i和j的取值范围是从1到8。当某个皇后占了位置(i,j)时,在这个位置的垂直方向、水平方向和斜线方向都不能再放其它皇后了。用语句实现,可定义如下三个整型数组:a[8],b[15],c[24]。其中:   a[j-1]=1 第j列上无皇后   a[j-1]=0 第j列上有皇后   b[i+j-2]=1 (i,j)的对角线(左上至右下)无皇后   b[i+j-2]=0 (i,j)的对角线(左上至右下)有皇后   c[i-j+7]=1 (i,j)的对角线(右上至左下)无皇后   c[i-j+7]=0 (i,j)的对角线(右上至左下)有皇后   (b)为第i个皇后选择位置的算法如下:   for(j=1;j<=8;j++) /*第i个皇后在第j行*/   if ((i,j)位置为空)) /*即相应的三个数组的对应元素值为1*/   {占用位置(i,j) /*置相应的三个数组对应的元素值为0*/   if i<8   为i+1个皇后选择合适的位置;   else 输出一个解   }   (2)图形存取   在Turbo C语言中,图形的存取可用如下标准函数实现:   size=imagesize(x1,y1,x2,y2) ;返回存储区域所需字节数。   arrow=malloc(size);建立指定大小的动态区域位图,并设定一指针arrow。   getimage(x1,y1,x2,y2,arrow);将指定区域位图存于一缓冲区。   putimage(x,y,arrow,copy)将位图置于屏幕上以(x,y)左上角的区域。   (3)程序清单如下   #include <graphics.h>   #include <stdlib.h>   #include <stdio.h>   #include <dos.h>   char n[3]={'0','0'};/*用于记录第几组解*/   int a[8],b[15],c[24],i;   int h[8]={127,177,227,277,327,377,427,477};/*每个皇后的行坐标*/   int l[8]={252,217,182,147,112,77,42,7}; /*每个皇后的列坐标*/   void *arrow;   void try(int i)   {int j;   for (j=1;j<=8;j++)   if (a[j-1]+b[i+j-2]+c[i-j+7]==3) /*如果第i列第j行为空*/   {a[j-1]=0;b[i+j-2]=0;c[i-j+7]=0;/*占用第i列第j行*/   putimage(h[i-1],l[j-1],arrow,COPY_PUT);/*显示皇后图形*/   delay(500);/*延时*/   if(i<8) try(i+1);   else /*输出一组解*/   {n[1]++;if (n[1]>'9') {n[0]++;n[1]='0';}   bar(260,300,390,340);/*显示第n组解*/   outtextxy(275,300,n);   delay(3000);   }   a[j-1]=1;b[i+j-2]=1;c[i-j+7]=1;   putimage(h[i-1],l[j-1],arrow,XOR_PUT);/*消去皇后,继续寻找下一组解*/   delay(500);   }}   int main(void)   {int gdrive=DETECT,gmode,errorcode;   unsigned int size;   initgraph(&gdrive,&gmode,"");   errorcode=graphresult();   if (errorcode!=grOk)   {printf("Graphics error\n");exit(1);}   rectangle(50,5,100,40);   rectangle(60,25,90,33);   /* 画皇冠 */   line(60,28,90,28);line(60,25,55,15);   line(55,15,68,25);line(68,25,68,10);   line(68,10,75,25);line(75,25,82,10);   line(82,10,82,25);line(82,25,95,15);   line(95,15,90,25);   size=imagesize(52,7,98,38); arrow=malloc(size);   getimage(52,7,98,38,arrow); /* 把皇冠保存到缓冲区 */   clearviewport();   settextstyle(TRIPLEX_FONT, HORIZ_DIR, 4);   setusercharsize(3, 1, 1, 1);   setfillstyle(1,4);   for (i=0;i<=7;i++) a=1;   for (i=0;i<=14;i++) b=1;   for (i=0;i<=23;i++) c=1;   for (i=0;i<=8;i++) line(125,i*35+5,525,i*35+5); /* 画棋盘 */   for (i=0;i<=8;i++) line(125+i*50,5,125+i*50,285);   try(1); /* 调用递归函数 */   delay(3000);   closegraph();   free(arrow);   }   二、循环实现 Java   /*   * 8皇后问题:   *   * 问题描述:   * 在一个8×8的棋盘里放置8个皇后,要求每个皇后两两之间不相冲突   *(在每一横列,竖列,斜列只有一个皇后)。   *   * 数据表示:   * 用一个 8 位的 8 进制数表示棋盘上皇后的位置:   * 比如:45615353 表示:   * 第0列皇后在第4个位置   * 第1列皇后在第5个位置   * 第2列皇后在第6个位置   * 。。。   * 第7列皇后在第3个位置   *   * 循环变量从 00000000 加到 77777777 (8进制数)的过程,就遍历了皇后所有的情况   * 程序中用八进制数用一个一维数组 data[] 表示   *   * 检测冲突:   * 横列冲突:data == data[j]   * 斜列冲突:(data+i) == (data[j]+j) 或者 (data-i) == (data[j]-j)   *   * 好处:   * 采用循环,而不是递规,系统资源占有少   * 可计算 n 皇后问题   * 把问题线性化处理,可以把问题分块,在分布式环境下用多台计算机一起算。   *   * ToDo:   * 枚举部分还可以进行优化,多加些判断条件速度可以更快。   * 输出部分可以修改成棋盘形式的输出   *   * @author cinc 2002-09-11   *   */   public class Queen {   int size;   int resultCount;   public void compute ( int size ) {   this.size = size;   resultCount = 0;   int data[] = new int[size];   int count; // 所有可能的情况个数   int i,j;   // 计算所有可能的情况的个数   count = 1;   for ( i=0 ; i<size ; i++ ) {   count = count * size;   }   // 对每一个可能的情况   for ( i=0 ; i<count ; i++ ) {   // 计算这种情况下的棋盘上皇后的摆放位置,用 8 进制数表示   // 此处可优化   int temp = i;   for ( j=0 ; j<size ; j++ ) {   data [j] = temp % size;   temp = temp / size;   }   // 测试这种情况是否可行,如果可以,输出   if ( test(data) )   output( data );   }   }   /*   * 测试这种情况皇后的排列是否可行   *   */   public boolean test( int[] data ) {   int i,j;   for ( i=0 ; i<size ; i++ ) {   for ( j=i+1 ; j<size ; j++ ) {   // 测试是否在同一排   if ( data == data[j] )   return false;   // 测试是否在一斜线   if ( (data+i) == (data[j]+j) )   return false;   // 测试是否在一反斜线   if ( (data-i) == (data[j]-j) )   return false;   }   }   return true;   }   /*   * 输出某种情况下皇后的坐标   *   */   public void output ( int[] data ) {   int i;   System.out.print ( ++resultCount + ": " );   for ( i=0 ; i<size ; i++ ) {   System.out.print ( "(" + i + "," + data + ") " );   }   System.out.println ();   }   public static void main(String args[]) {   (new Queen()).compute( 8 );   }   }   三、八皇后问题的Qbasic版的解决方案   10 I = 1   20 A(I) = 1   30 G = 1   40 FOR K = I - 1 TO 1 STEP -1   50 IF A(I) = A(K) THEN 70   60 IF ABS(A(I) - A(K)) <> I - K THEN 90   70 G = 0   80 GOTO 100   90 NEXT K   100 IF I <> 8 THEN 180   110 IF G = 0 THEN 180   120 FOR L = 1 TO 8   130 PRINT USING “##”; A(L);   140 NEXT L   150 PRINT “*”;   160 M = M + 1   170 IF M MOD 3 = 0 THEN PRINT   180 IF G = 0 THEN 230   190 IF I = 8 THEN 230   200 I = I + 1   210 A(I) = 1   220 GOTO 30   230 IF A(I) < 8 THEN 270   240 I = I - 1   250 IF I = 0 THEN 290   260 GOTO 230   270 A(I) = A(I) + 1   280 GOTO 30   290 PRINT   300 PRINT “SUM=”; USING “##”; M;   310 PRINT   320 END   四、八皇后问题的高效解法-递归版   //8 Queen 递归算法   //如果有一个Q 为 chess=j;   //则不安全的地方是 k行 j位置,j+k-i位置,j-k+i位置   class Queen8{   static final int QueenMax = 8;   static int oktimes = 0;   static int chess[] = new int[QueenMax];//每一个Queen的放置位置   public static void main(String args[]){   for (int i=0;i<QueenMax;i++)chess=-1;   placequeen(0);   System.out.println("\n\n\n八皇后共有"+oktimes+"个解法 made by yifi 2003");   }   public static void placequeen(int num){ //num 为现在要放置的行数   int i=0;   boolean qsave[] = new boolean[QueenMax];   for(;i<QueenMax;i++) qsave=true;   //下面先把安全位数组完成   i=0;//i 是现在要检查的数组值   while (i<num){   qsave[chess]=false;   int k=num-i;   if ( (chess+k >= 0) && (chess+k < QueenMax) ) qsave[chess+k]=false;   if ( (chess-k >= 0) && (chess-k < QueenMax) ) qsave[chess-k]=false;   i++;   }   //下面历遍安全位   for(i=0;i<QueenMax;i++){   if (qsave==false)continue;   if (num<QueenMax-1){   chess[num]=i;   placequeen(num+1);   }   else{ //num is last one   chess[num]=i;   oktimes++;   System.out.println("这是第"+oktimes+"个解法 如下:");   System.out.println("第n行: 1 2 3 4 5 6 7 8");   for (i=0;i<QueenMax;i++){   String row="第"+(i+1)+"行: ";   if (chess==0);   else   for(int j=0;j<chess;j++) row+="--";   row+="++";   int j = chess;   while(j<QueenMax-1){row+="--";j++;}   System.out.println(row);   }   }   }   //历遍完成就停止   }   }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值