Python:通过随机游走解决八皇后问题


1 八皇后问题

有一个8乘8的棋盘,现在要将八个皇后放到棋盘上,满足:对于每一个皇后,在自己所在的行、列、两个对角线都没有其他皇后。
在这里插入图片描述
规定初始状态为【空棋盘】,动作为【每次只在最左面未放置皇后的列上放一个皇后】。这样就使得棋盘的同列最多只能出现一个皇后。


2 程序代码

2.1 程序1

程序1:functions.py。包括两个函数:attacked_queens_pairs, display_board,分别完成【计算序列对应棋盘的互相攻击的皇后对数】和【打印输出序列对应的棋盘】的功能。如下:

import numpy as np

def attacked_queens_pairs(seqs, pos):
    """
    计算序列对应棋盘的【互相攻击的皇后对数n】
    因为主程序只在最左面未放置皇后的列上放一个皇后,因此只需检查放置了皇后的前几列
    参数pos的作用是限制只统计前pos列【互相攻击的皇后对数n】
    并且只需要检查当前棋盘的皇后在各自的行和两条对角线上是否有其他皇后
    """
    a = np.array([0] * 81)  # 创建一个有81个0的一维数组
    a = a.reshape(9, 9)  # 改为9*9二维数组。为方便后面使用,只用后八行和后八列的8*8部分,作为一个空白棋盘
    n = 0  # 互相攻击的皇后对数初始化为0

    for i in range(1, pos+1):
        a[seqs[i - 1]][i] = 1  # 根据序列的前pos个数,按从第1列到pos列的顺序,在空白棋盘对应位置放一个皇后,生成当前序列对应的棋盘

    for i in range(1, pos+1): # 只统计前pos列【互相攻击的皇后对数n】
        for k in list(range(1, i)) + list(range(i + 1, 9)):  # 检查每个皇后各自所在的行上是否有其他皇后
            if a[seqs[i - 1]][k] == 1:  # 有其他皇后
                n += 1
        t1 = t2 = seqs[i - 1]
        for j in range(i - 1, 0, -1):  # 看左半段的两条对角线
            if t1 != 1:
                t1 -= 1
                if a[t1][j] == 1:
                    n += 1  # 正对角线左半段上还有其他皇后

            if t2 != 8:
                t2 += 1
                if a[t2][j] == 1:
                    n += 1  # 次对角线左半段上还有其他皇后

        t1 = t2 = seqs[i - 1]
        for j in range(i + 1, 9):  # 看右半段的两条对角线
            if t1 != 1:
                t1 -= 1
                if a[t1][j] == 1:
                    n += 1  # 正对角线右半段上还有其他皇后

            if t2 != 8:
                t2 += 1
                if a[t2][j] == 1:
                    n += 1  # 次对角线右半段上还有其他皇后
    return int(n/2)  # 返回n/2,因为A攻击B也意味着B攻击A,因此返回n的一半

def display_board(seqs):
    """
     显示序列对应的棋盘
    """
    board = np.array([0] * 81)  # 创建一个有81个0的一维数组
    board = board.reshape(9, 9)  # 改变为9*9二维数组,为了后面方便使用,只用后八行和后八列的8*8部分,作为一个空白棋盘

    for i in range(1, 9):
        board[seqs[i - 1]][i] = 1  # 根据序列,从第一列到最后一列的顺序,在对应位置放一个皇后,生成当前序列对应的棋盘
    print('对应棋盘如下:')
    for i in board[1:]:
        for j in i[1:]:
            print(j, ' ', end="")  # 有了end="",print就不会换行
        print()  # 输出完一行后再换行,这里不能是print('\n'),否则会换两行
    print('攻击的皇后对数为' + str(attacked_queens_pairs(seqs,8)))

此程序无任何输出,只是定义了2个函数以供主程序调用。

2.2 程序2

程序2:main.py。为主程序,通过调用程序2的两个函数,完成随机游走解决八皇后问题的全过程。如下:

import random
from functions import attacked_queens_pairs, display_board

seqs = [0] * 8 # 初始化序列
flag = 0 # 失败标志

for i in range(8):
    nums = list(range(1, 9)) # 从nums中选择在当前列放置皇后的位置
    if flag == 1: # 失败则结束循环
        break
    while True:
        try:
            temp = random.choice(nums) # 随机挑选,即随机游走
            seqs[i] = temp # 暂时将皇后放在当前列的第temp行
            nums.remove(temp) # 从nums移除已产生的随机数
            if attacked_queens_pairs(seqs, i+1) == 0: # 将皇后放在当前列的第temp行后,若已放置皇后的前i+1列中无互相攻击的皇后,则进行下一列位置的选择
                break
        except IndexError:
            print('本次计算失败,请重新运行本程序!')
            flag = 1 # 已失败
            break

if flag == 0:
    print('已找到解序列:' + str(seqs))
    display_board(seqs)

一种输出如下:

已找到解序列:[7, 1, 3, 8, 6, 4, 2, 5]
对应棋盘如下:
0  1  0  0  0  0  0  0  
0  0  0  0  0  0  1  0  
0  0  1  0  0  0  0  0  
0  0  0  0  0  1  0  0  
0  0  0  0  0  0  0  1  
0  0  0  0  1  0  0  0  
1  0  0  0  0  0  0  0  
0  0  0  1  0  0  0  0  
攻击的皇后对数为0

另一种输出如下:

本次计算失败,请重新运行本程序!

上面列出了两种输出,第一种找到了解序列,第二种没找到。因为算法是将皇后随机放置在当前列的任意位置,若已放置皇后的前几列中无互相攻击的皇后,则进行下一列位置的选择,因此过程中可能会达到【无合适位置可放】的状态,不得不宣告失败,只能通过重新运行程序来重新进行探索。


3 评价

主程序简短易懂,但是要得到解可能需要多运行几次,如果失败了不是您的原因,重新运行就好了。


END

猜你喜欢

转载自blog.csdn.net/qq_40061206/article/details/111729406
今日推荐