算法八皇后问题课程设计

本博客以及课程设计内容参考于《趣学算法》书以及以下几个博客。

https://blog.csdn.net/sinat_42559225/article/details/104363860Python解决八皇后问题并输出可视化结果

https://blog.csdn.net/weixin_44227192/article/details/106931823回溯法求解八皇后问题(python实现)

https://blog.csdn.net/weiyuefei/article/details/79316653[回溯算法] 五大常用算法之回溯法

排名不分先后 为了使课设和内容相对丰满 查阅了菜鸟教程和b站的https://www.bilibili.com/video/BV1ZK411K7A8?from=search&seid=3467690056833699013算法与数据结构,回溯法求解八皇后,最经典的递归问题

虽然运用的不多 但问题的功能讲解的很详细。

感谢各位大佬码农的帮助!以下便是课设报告书以及自己的一些感悟 课设由小组内三人共同完成 感谢大家的付出!

 

 

 

一、问题分析

1.1 课设内容

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

1.2 课设要求

要求:利用回溯法求解八皇后问题,输出该问题的全部解,实现可视化展示。

以上是本次课程设计的要求。

二、算法与解决方案

2.2 问题思路

通过回溯法得出皇后的位置,通过python的matplotlib库绘制棋盘。

2.3 功能结构与方案

 

2.3.1回溯法方面

2.3.1.1 回溯法

回溯法思路的简单描述是:把问题的解空间转化成了图或者树的结构表示,然后使用深度优先搜索策略进行遍历,遍历的过程中记录和寻找所有可行解或者最优解。

简单来说,A→B,C,D  B→E,F  C→G,H  D→I,J以此类推,若满足则继续遍历,不满足则返回去换条路径遍历。

2.3.1.2 应用于本问题

如图2.4所示,每一行可以而且必须放一个皇后,所以n皇后问题的解可以用一个n元向量X=(x1,x2,.....xn)表示,其中,1≤i≤n且1≤xi≤n,即第n个皇后放在第i行第xi列上。

图2.4 

由于两个皇后不能放在同一列上,所以,解向量X必须满足的约束条件为: xi≠xj;若两个皇后的摆放位置分别是(i,xi)和(j,xj),在棋盘上斜率为-1的斜线上,满足条件i-j = xi-xj; 在棋盘上斜率为1的斜线上,满足条件i+j=xi+xj;

综合两种情况,由于两个皇后不能位于同一斜线上,所以,解向量X必须满足的约束条件为: |i-xi| ≠ |j-xj|

如图2.4是一个8x8的表格图片,皇后的位置由第一行第一个开始放置,之后第一行和第一列以及第一行第一列[坐标暂为(1,1)]所在对角线不可再放置皇后,图2-5为其中一种解法。随着皇后的放置可供放置皇后的位置越来越少,当图中放够八个皇后时方为正解。

图2.5

我们可以把问题简化到四皇后问题。如图2.6

图2.6

树的深度表示考虑棋盘的行数,树深度为1表示考虑棋盘的第一行等等,每个节点表示棋盘的一个状态,节点的子节点表示在一个棋盘状态下考虑棋盘的下一行的皇后摆放状态,同一深度的节点从左到右分别表示皇后摆放在这一行的不同位置(列)上;

树的遍历顺序为向下和向右。

开始时深度优先遍历这棵树,如果能到达叶节点,则说明棋盘的所有行都按要求放上了皇后,即找到了一个可行解,因为一共就4列,所以此时每一列都有皇后了,挪动第四行的皇后必然会导致两个皇后出现在同一列,因此无法通过访问可行解的兄弟节点再得到可行解,此时应返回上一行,挪动上一行皇后的位置;

若某一行所有位置均不可行,则返回上一行,向右挪动皇后的位置,即访问父节点的右兄弟节点,最终函数再根节点返回,算法结束。

2.3.2 可视化绘图方面

2.3.2.1 matplotlib库

通过 Matplotlib,开发者可以仅需要几行代码,便可以生成绘图,直方图,功率谱,条形图,错误图,散点图等。

配置参数:

axex: 设置坐标轴边界和表面的颜色、坐标刻度值大小和网格的显示

figure: 控制dpi、边界颜色、图形大小、和子区( subplot)设置

font: 字体集(font family)、字体大小和样式设置

grid: 设置网格颜色和线性

legend: 设置图例和其中的文本的显示

line: 设置线条(颜色、线型、宽度等)和标记

patch: 是填充2D空间的图形对象,如多边形和圆。控制线宽、颜色和抗锯齿设置等。

savefig: 可以对保存的图形进行单独设置。例如,设置渲染的文件的背景为白色。

verbose: 设置matplotlib在执行期间信息输出,如silent、helpful、debug和debug-annoying。

xticks和yticks: 为x,y轴的主刻度和次刻度设置颜色、大小、方向,以及标签大小。

 

2.3.2.2 绘制棋盘

参考于https://blog.csdn.net/weixin_45405128/article/details/102510877

本文这里对代码进行注释

import matplotlib #引入库

color1=(1,1,1)#选择rgb对应黑色

color2=(247/255,220/255,111/255) #同上设置颜色#F7DC6F中等亮度黄色

 

mat=np.zeros((8,8))   #返回一个8个每个数组中有8个0的数组

for i in range(8):

    for j in range(8):

        if (i+j)%2==0:

            mat[i,j]=1     #如果行列数加起来可以被2整除则赋值为1

        else:

            mat[i,j]=-1    #反之为-1

 

my_cmap=matplotlib.colors.LinearSegmentedColormap.from_list('my_camp',[color1,color2],2)    #填充

cs=plt.imshow(mat,cmap=my_cmap) 

 

plt.xticks(np.linspace(0,8,8,endpoint=False),('a','b','c','d','e','f','g','h'),fontsize=20) #x坐标

plt.yticks(np.linspace(0,8,8,endpoint=False),('1','2','3','4','5','6','7','8'),fontsize=20)#y坐标

plt.tick_params(bottom=False,left=False,labeltop=True,labelright=True) #坐标的分布

plt.show()     #生成图像

 

三、算法设计过程中遇到的问题及分析解决方案

3.1字体问题导致的运行报错

在设定中文字符集,解决绘图时中文乱码问题时,我们遇到了运行报错。如图所示,所显示的报错意为未找到相应字体。

图3.1.1

为了解决此问题,我们搜索了CSDN,具体步骤为:

  1. 找到Mircosoft YaHei UI字体文件

一般在C:\Windows\Fonts 里可以看到,如下图3.2

图3.1.2

2.将字体文件拷贝至下面的文件夹。里自己的python安装目录F:\Python3.7\Lib\site-packages\matplotlib\mpl-data\fonts\ttf

图3.1.3 

拷贝完成后,该文件夹里会有MSYH.TTC 文件

3.修改matplotlibrc文件

打开matplotlibrc文件

(1)删除 font.family 前面的 # 并将冒号后面改为“Microsoft YaHei”;

(2)删除font.sans-serif 前面的 # 并在冒号后面添加“Microsoft YaHei

图3.1.4

图3.1.5 

修改完成后保存,将python关闭,重新启动运行程序

4.成功解决

3.2 输出结果格式不清晰明确

在运行结果中,输出的列表中的每个数表示的是纵向坐标即列坐标,横向坐标默认为从1至8, 如图:

图3.2.1 

       我们将原先代码print("第"+str(num)+"种:",queen_list2),修改为print("第"+str(num)+"种:",(1,queen_list2[0]),(2,queen_list2[1]),(3,queen_list2[2]),(4,queen_list2[3]),(5,queen_list2[4]),(6,queen_list2[5]),(7,queen_list2[6]),(8,queen_list2[7]))

       之后,我们成功输出了(x, y)格式的坐标值

图3.2.2

3.3棋盘的可视化问题

对于八皇后问题的求解,输出坐标已经可以实现了,对应的92种解都可以用 (x, y)坐标表示法表示出来,但接下来需要实现的则是最关键的可视化部分,我们需要利用matplotlib和numpy库来实现一张8*8黑白棋盘,并用不同颜色(这里我们使用红色)来对每个皇后的位置进行标注。棋盘绘制好之后保存求解的92种结果。这里参考了代码https://blog.csdn.net/weixin_45405128/article/details/102510877

大致步骤为:

  1. 定义全局变量num

global num

  1. 利用numpy库制作一个8*8的网格

mat=np.zeros((8,8))

    for i in range(8):

        for j in range(8):

            if result[i]==j:

                mat[i,j]=1

            elif (i+j)%2==0:

                mat[i,j]=-1

            else:

                mat[i,j]=0

  1. 使用matplotlib库对网格上色

my_cmap=matplotlib.colors.LinearSegmentedColormap.from_list('my_camp',['white','black','red'],3)

    plt.imshow(mat,cmap=my_cmap)

  1. 标注表头

plt.title("第"+str(num)+"种解法",fontsize=16)

  1. 创建保存路径

plt.xticks([])

    plt.yticks([])

plt.savefig('C:/Users/zhous/Desktop/EightQueens Palace/'+str(num)+'.png') #保存图片的路径,自行修改

以下是生成结果之一:

图3.3.1

四、算法设计特色及关键技术

4.1八皇后问题分析及解决思路

在n×n的棋盘上放置彼此不受攻击的n个皇后。按照国际象棋的规则,皇后可以攻击与之在同一行、同一列、同一斜线上的棋子。现在在n×n的棋盘上放置n个皇后,使彼此不受攻击。

我们在第i行第j列放置一个皇后,那么第i行的其他位置(同行),那么第j列的其他位置(同列),同一斜线上的其他位置,都不能再放置皇后。

条件是这样要求的,但是我们不可能杂乱无章地尝试每个位置,要有求解策略。我们可以以行为主导:

·在第1行第1列放置第1个皇后。

·在第2行放置第2个皇后。第2个皇后的位置不能和第1个皇后同列、同斜线,不用再判断是否同行了,因为我们每行只放置一个,本来就已经不同行。

·在第3行放置第3个皇后,第3个皇后的位置不能和前2个皇后同列、同斜线。

·在第t行放置第t个皇后,第t个皇后的位置不能和前t-1个皇后同列、同斜线。

4.2算法设计特色

回溯法是一种选优搜索法,按照选优条件深度优先搜索,以达到目标。当搜索到某一步时,发现原先选择并不是最优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术称为回溯法,而满足回溯条件的某个状态称为“回溯点”。

回溯法是从初始状态出发,按照深度优先搜索的方式,根据产生子结点的条件约束,搜索问题的解。当发现当前结点不满足求解条件时,就回溯,尝试其他的路径。回溯法是一种“能进则进,进不了则换,换不了则退”的搜索方法。

4.3算法关键技术

(1)定义问题的解空间

n皇后问题解的形式为n元组: {x,x,…,X,…,xn},分量x,表示第i个皇后放置在第﹔行第x;列,x,的取值为1,2,…,n。例如x=5,表示第2个皇后放置在第2行第5列。显约束为不同行。

(2)解空间的组织结构

n皇后问题的解空间是一棵m (m=n)叉树,树的深度为n,如图所示。

图4.3.1

(3)搜索解空间

·约束条件

在第t行放置第t个皇后时,第t个皇后的位置不能和前t-1个皇后同列、同斜线。第i个皇后和第j个皇后不同列,即x!=xj,并且不同斜线|i一j != X一x]。

·限界条件

该问题不存在放置方案好坏的情况,所以不需要设置限界条件。

·搜索过程

从根开始,以深度优先搜索的方式进行搜索。根结点是活结点,并且是当前的扩展结点。在搜索过程中,当前的扩展结点沿纵深方向移向一个新结点,判断该新结点是否满足隐约束。如果满足,则新结点成为活结点,并且成为当前的扩展结点,继续深一层的搜索;如果不满足,则换到该新结点的兄弟结点继续搜索;如果新结点没有兄弟结点,或其兄弟结点已全部搜索完毕,则扩展结点成为死结点,搜索回溯到其父结点处继续进行。搜索过程直到找到问题的根结点变成死结点为止。

五、算法测试

5.1程序功能

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

            

 

​​​​​

​​​​​​图5.1.1

以上为全部解

 

图5.1.2

将旋转和对称的解归为一种有12个独立解

图5.1.3 

5.2算法性能测试

程序运行耗时3.5076117515563965s

图5-2程序运行时间

图5.2 

 

六、结论

6.1完成情况

八皇后问题所有解已经全部解出:

八个皇后在8x8棋盘上共有4,426,165,368种摆放方法,只有92个互不相同的解,且完成了八皇后解法的坐标和绘图可视化,保留了解法视图。

6.2有待改进之处

八皇后问题可以延申到n皇后问题,我们使用的程序代码并不是效率最高的,程序代码还可以使用bit数组来代替以前由int或者bool数组来存储当前格子被占用或者说可用信息,从这可以看出N个皇后对应需要N位表示。
        以前我们需要在一个N*N正方形的网格中挪动皇后来进行试探回溯,每走一步都要观察和记录一个格子前后左右对角线上格子的信息;采用bit位进行信息存储的话,就可以只在一行格子也就是(1行×N列)个格子中进行试探回溯即可,对角线上的限制被化归为列上的限制。
         程序中主要需要下面三个bit数组,每位对应网格的一列,在C中就是取一个整形数的某部分连续位即可。 row用来记录当前哪些列上的位置不可用,也就是哪些列被皇后占用,对应为1。ld,rd同样也是记录当前哪些列位置不可用,但是不表示被皇后占用,而是表示会被已有皇后在对角线上吃掉的位置。这三个位数组进行“或”操作后就是表示当前还有哪些位置可以放置新的皇后,对应0的位置可放新的皇后。 所有下一个位置的试探过程都是通过位操作来实现的,这是借用了C语言的好处,详见代码注释。关于此算法,如果考虑N×N棋盘的对称性,对于大N来说仍能较大地提升效率!

位操作优点:
    1、以前都是用数组来描述状态,而这算法采用是的位来描述,运算速度可以大大提升,以后写程序对于描述状态的变量大家可以借鉴这个例子,会让你的程序跑得更快  
    2、描述每行可放置的位置都是只用row,ld,rd这3个变量来描述,这样使得程序看起来挺简洁的。

6.3心得体会

这次课程设计我们解决了八皇后问题的位置输出和可视化,完成了八皇后92种解法利用回溯法解决了一系列问题。

回溯的处理思想,有点类似枚举搜索。我们枚举所有的解,找到满足期望的解。为了有规律地枚举所有可能的解,避免遗漏和重复,我们把问题求解的过程分为多个阶段。每个阶段,我们都会面对一个岔路口,我们先随意选一条路走,当发现这条路走不通的时候(不符合期望的解),就回退到上一个岔路口,另选一种走法继续走。笼统地讲,回溯算法很多时候都应用在“搜索”这类问题上,在一组可能的解中,搜索满足期望的解。

在我们的一生中,会遇到很多重要的岔路口。在岔路口上,每个选择都会影响我们今后的人生。有的人在每个岔路口都能做出最正确的选择,最后生活、事业都达到了一个很高的高度;而有的人一路选错,最后碌碌无为。如果人生可以量化,那如何才能在岔路口做出最正确的选择,让自己的人生“最优”呢?

 

以上为完整的课程设计报告书 具体代码大家可以参照开头的博客 我放出我们使用的代码

import matplotlib.pyplot as plt
import matplotlib
import numpy as np
# ------------------------
#设定中文字符集,解决绘图时中文乱码问题
matplotlib.rcParams['font.sans-serif'] = ['SimHei']

#放置函数
def Queen_set(n):
    global num
    queen_list2=[i+1 for i in queen_list]

    # for j in range(0,8):
    #     queen_list3=[(j,queen_list2[0])]
    if n==8:
        print("第"+str(num)+"种:",(1,queen_list2[0]),(2,queen_list2[1]),(3,queen_list2[2]),(4,queen_list2[3])
        ,(5,queen_list2[4]),(6,queen_list2[5]),(7,queen_list2[6]),(8,queen_list2[7]))

        plot_chess(queen_list)
        num+=1
        return
    else:
        for i in range(8):
            if check(n,i):
                queen_list.append(i)    #在当前位置放置皇后
                Queen_set(n+1)  #递归,进入下一层搜索
                queen_list.pop() #回溯关键点,清除前一步的错误数据


#检测当前位置是否符合要求
#depth:搜索深度 index:目前的摆放情况
def check(depth,index):
    if depth==0:
        return True
    else:
        for i in range(depth):
            if queen_list[i] == index:   #判断列是否符合
                return False
            #判断对角线规则是否符合
            elif index==queen_list[i]-depth+i or index==queen_list[i]+ depth-i:
                return False
        return True

# 绘制棋盘并保存求解结果
def plot_chess(result):
    global num
    mat=np.zeros((8,8))
    for i in range(8):
        for j in range(8):
            if result[i]==j:
                mat[i,j]=1
            elif (i+j)%2==0:
                mat[i,j]=-1
            else:
                mat[i,j]=0
    my_cmap=matplotlib.colors.LinearSegmentedColormap.from_list('my_camp',['white','black','deeppink'],3)
    plt.imshow(mat,cmap=my_cmap)
    plt.title("第"+str(num)+"种解法",fontsize=16)
    plt.xticks([])
    plt.yticks([])
    plt.savefig('D:/2021家里蹲/甚么?你是龙王!/算法期末课设/课设/picture'+str(num)+'.png') #保存图片的路径,自行修改

queen_list = []
num = 1
Queen_set(0)

和第一个博客的几乎一样   因为可视化也是从他哪里学的  因为设计思路的相同 代码也几乎是照着打的  当然字体和颜色可自己选择更改以及文件路径根据实际情况需要进行更改。

期间也写了别的代码 但目前这个是我认为最好的  之后有新的更新再发布把!

  • 5
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值