摘 要
八皇后问题是一个古老而著名的问题,是回溯算法的典型例题。在 8*8 格的 国际象棋棋盘上,安放八个皇后,要求没有一个皇后能够“吃掉”任何其他一个 皇后,即任意两个皇后都不能处于同一行、同一列或同一条对角线上,求解有多 少种摆法。有人用图论的方法得出结论,有 92 种摆法。
本文研究的课题是,设计算法、编写程序求得八皇后问题可行的方案。本文 采用回溯算法结合递归的思想进行程序的设计:将一个皇后按照规则从首行开始 放置,然后在下一行按规则放置皇后,重复上述步骤,当出现错误解时回退至上 一行或者上多行重新放置。最终达成“放得下八个皇后”及“八个皇后不互相吃” 的条件的都是八皇后问题的解。
关键词:八皇后问题 回溯算法 递归 Java GUI界面
第一章 绪 论
1.1 课设主要研究问题
八皇后问题是一个古老而著名的问题,是回溯算法的典型例题。该问题是十 九世纪著名的数学家高斯 1850 年提出:在 8*8 格的国际象棋棋盘上,安放 8 个 皇后,要求没有一个皇后能够“吃掉”任何其他一个皇后,即任意两个皇后都不 能处于同一行、同一列或同一条对角线上,求解有多少种摆法。高斯认为有 76 种方案。1854 年在柏林的象棋杂志上不同的作者发表了 40 种不同的解,后来有人用图论的方法得出结论,有 92 种摆法。
1.2 课设应用的理论知识
1.2.1 理论知识
本次课程设计中应用的理论知识有:回溯算法、递归思想、Java语言循环语句 for 语句的灵活运用、Java语言条件语句 if——else 的运用、数据结构中树知识的灵活运用以及栈、数组的掌握。 回溯算法:解决一个回溯问题,实际上就是一个决策树的遍历过程。
解决 3 个问题: 1、路径:也就是已经做出的选择。
2、选择列表:也就是你当前可以做的选择。
3、结束条件:也就是到达决策树底层,无法再做选择的条件。
1.2.2 程序算法
八皇后问题是栈应用的另一个典型例子。在此问题中我用到栈的数据结构来实现回溯法,递归到底部然后回溯到保存之前信息的栈空间,然后执行pollLast的语句,取出棋子。假如有一行的每一列都放不下符合规范的棋子,那么for循环结束后就会自动返回之前的栈空间;递归中每一层,也就是每次进入一个方法体后我都会创建一个新的栈空间。
这个程序的回溯法巧妙在:每一行中可能有n列是符合条件的,但是如果最终的皇后数不到8的话,我们需要回溯,执行pollLast语句,然后继续之前栈空间的for循环,然后依次类推。一旦我们完成了八个皇后的放置的时候,此时一定是在第八行,我们继续执行for循环直到结束,然后回到上一个递归调用方法的帧栈继续执行for循环;直到棋子的所有摆放情况都已经遍历完了,最终我们的虚拟棋盘会变为空。
第二章 课设实现过程
2.1 实现所需算法
回溯算法(用递归的方法实现)
1、概念
回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到
某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不
通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
回溯法是一个既带有系统性又带有跳跃性的的搜索算法。它在包含问题的所
有解的解空间树中,按照深度优先的策略,从根结点出发搜索解空间树。算法搜
索至解空间树的任一结点时,总是先判断该结点是否肯定不包含问题的解。如果
肯定不包含,则跳过对以该结点为根的子树的系统搜索,逐层向其祖先结点回溯。
否则,进入该子树,继续按深度优先的策略进行搜索。回溯法在用来求问题的所
有解时,要回溯到根,且根结点的所有子树都已被搜索遍才结束。而回溯法在用
来求问题的任一解时,只要搜索到问题的一个解就可以结束。这种以深度优先的
方式系统地搜索问题的解的算法称为回溯法,它适用解一些组合数较大的问题。
2、基本思想
对于用回溯法求解的问题,首先要将问题进行适当的转化,得出状态空间树。
这棵树的每条完整路径都代表了一种解的可能。通过深度优先搜索这棵树,枚举
每种可能的解的情况;从而得出结果。但是,回溯法中通过构造约束函数,可以
大大提升程序率,因为在深度优先搜索的过程中,不断的将每个解(并不一定是
完整的,事实上这也就是构造约束函数的意义所在)与约束函数进行对照从而删
除一些不可能的解,这样就不必继续把解的剩余部分列出从而节省部分时间。
回溯法中,首先需要明确下面三个概念:
(1)约束函数:约束函数是根据题意定出的。通过描述合法解的一般特征用于去 除不合法的解,从而避免继续搜索出这个不合法解的剩余部分。因此,约束函数是对于任何状态空间树上的节点都有效、等价的。
(2)状态空间树:状态空间树是一个对所有解的图形描述。树上的每个子节点的解都只有一个部分与父节点同。
(3)扩展节点、活结点、死结点:所谓扩展节点,就是当前正在求出它的子节点 的节点,在 DFS 中,只允许有一个扩展节点。活结点就是通过与约束函数的对照, 节点本身和其父节点均满足约束函数要求的节点;死结点反之。由此很容易知道 死结点是不必求出其子节点的(没有意义)。
3、解题步骤
(1)描述解的形式,定义一个解空间,它包含问题的所有解,这一步主要明确问 题的解空间树。
(2)构造状态空间树。
(3)构造约束函数(用于杀死节点)。然后就要通过 DFS 思想完成回溯,具体流程如下:
(1)设置初始化的方案(给变量赋初值,读入已知数据等)。
(2)变换方式去试探,若全部试完则转(7)。
(3)判断此法是否成功(通过约束函数),不成功则转(2)。
(4)试探成功则前进一步再试探。
(5)正确方案还未找到则转(2)。
(6)已找到一种方案则记录并打印。
(7)退回一步(回溯),若未退到头则转(2)。
(8)已退到头则结束或打印无解
2.2 绘制程序框图
2.3运行程序截图
新方法和新思路
回溯法解决八皇后问题的过程中,并不需要记录整棵“搜索树”,而只需记录从初始状态到当前状态的一条搜索路径,是“线性链状”的,其最大优点是占用空间少。
利用WindowBuider进行八皇后问题的GUI动态演示。利用鼠标监听器MouseListener监听按钮的动作,并用Graphics类进行绘制GUI界面。
在进行八皇后问题求解的时候,我使用了链表进行操作。构造了Location类对链表进行操作,在该类中有棋子的x和y坐标的属性,并重写了toString(该方法在testArea中打印一个合适的被放置的棋子的具体位置)。当有合适棋子放置的时候调用offer方法,要移除棋子的时候调用pollLast方法。
使用ImageIcon 加载棋子 的图片,并调用该类的paintIcon方法绘制图片,调用Graphics类的clearRect方法清除图片
程序代码