学习目标:
用Python语言和回溯法解决八皇后棋盘问题,并实现可视化输出
学习内容:
问题分析
问题描述:八皇后问题,是一个古老而著名的问题,是回溯算法的典型案例。该问题是国际西洋棋棋手马克斯·贝瑟尔于1848年提出:在8×8格的国际象棋上摆放八个皇后,使其不能互相攻击,即任意两个皇后都不能处于同一行、同一列或同一斜线上,问有多少种摆法。 高斯认为有76种方案。1854年在柏林的象棋杂志上不同的作者发表了40种不同的解,后来有人用图论的方法解出92种结果。
课设要求
要求:利用回溯法求解八皇后问题,输出该问题的全部解,实现可视化展示。
以上是本次课程设计的要求。
算法思路与解决方案
回溯法特点
回溯法是一种选优搜索法,按照选优条件深度优先搜索,以达到目标。当搜索到某一步时,发现原先选择并不是最优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术称为回溯法,而满足回溯条件的某个状态称为“回溯点”。
回溯法是从初始状态出发,按照深度优先搜索的方式,根据产生子结点的条件约束,搜索问题的解。当发现当前结点不满足求解条件时,就回溯,尝试其他的路径。回溯法是一种“能进则进,进不了则换,换不了则退”的搜索方法。
算法关键技术
(1)定义问题的解空间
n皇后问题解的形式为n元组: {x1,x2,…,xi,…,xn},分量xi表示第i个皇后放置在第i行第xi列,xi,的取值为1,2,…,n。例如x2=5,表示第2个皇后放置在第2行第5列。显约束为不同行。
(2)解空间的组织结构
n皇后问题的解空间是一棵m (m=n)叉树,树的深度为n,如图所示。
(3)搜索解空间
· 约束条件
在第t行放置第t个皇后时,第t个皇后的位置不能和前t-1个皇后同列、同斜线。第i个皇后和第j个皇后不同列,即xi=xj,并且不同斜线|i一j| != [xi一xj]。
· 限界条件
该问题不存在放置方案好坏的情况,所以不需要设置限界条件。
· 搜索过程
从根开始,以深度优先搜索的方式进行搜索。根结点是活结点,并且是当前的扩展结点。在搜索过程中,当前的扩展结点沿纵深方向移向一个新结点,判断该新结点是否满足隐约束。如果满足,则新结点成为活结点,并且成为当前的扩展结点,继续深一层的搜索;如果不满足,则换到该新结点的兄弟结点继续搜索;如果新结点没有兄弟结点,或其兄弟结点已全部搜索完毕,则扩展结点成为死结点,搜索回溯到其父结点处继续进行。搜索过程直到找到问题的根结点变成死结点为止。
算法优化
1.算法复杂度分析
(1)时间复杂度
n皇后问题的解空间是一棵m (m=n)叉树,树的深度为n。最坏情况下,解空间树除了最后一层外,有1+n+n2+…+n(n-1)=(nn-1)(n-1)≈n(n-1)个结点需要扩展,而这些结点每个都要扩展n个分支,总的分支个数为n,每个分支都判断约束函数,判断约束条件需要O(n)的时间,因此耗时O(n(n-1))。在叶子结点处输出当前最优解需要耗时O(n),在最坏情况下回搜索到每一个叶子结点,叶子个数为nn,故耗时为O(n(n-1))。因此,时间复杂度为O(n(n-1))。
(2)空间复杂度
回溯法的另一个重要特性就是在搜索执行的同时产生解空间。在所搜过程中的任何时刻,仅保留从开始结点到当前扩展结点的路径,从开始结点起最长的路径为n。程序中我们使用x[]数组记录该最长路径作为可行解,所以该算法的空间复杂度为O(n)。
2.算法优化拓展
在上面的求解过程中,我们的解空间过于庞大,所以时间复杂度很高,算法效率当然会降低。解空间越小,算法效率越高。因为解空间是我们要搜索解的范围,就像大海捞针,难度很大,在一个水盆里捞针,难度就小了,如果在一个碗里捞针,就更容易了。
我们用四皇后问题来简化表示一下
四皇后问题,显约束为不同行的解空间树如图
显约束可以控制解空间大小,隐约束是在搜索解空间过程中判定可行解或最优解的。
例如x1=1的分支,x2就不能再等于1,因为这样就同列了。如果x1=1、x2=2,x3就不能再等于1、2,也就是说xt的值不能与前t-1个解的取值相同。每层结点产生的孩子数比上一层少一个。四皇后问题,显约束为不同行、不同列的解空间树如图所示。
我们可以清楚地看到解空间变小了好多,极大地降低了算法的时间复杂度,此时时间复杂度为O(n3)。
Python代码实现
这里参考了https://blog.csdn.net/weixin_44227192/article/details/106931823?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-0&spm=1001.2101.3001.4242 大佬的代码,并进行了部分修改
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]
if n==8:
print("第"+str(num)