一. 实践内容
隐式图的搜索问题
(1)实践任务
- 对九宫重排问题,建立图的启发式搜索求解方法;
- 用A*算法求解九宫重排问题。
(2)实践要求
- 3х3九宫棋盘,放置数码为1~8的8个棋子,棋盘中留有一个空格,空格周围的棋子可以移动到空格中,从而改变棋盘的布局。根据给定初始布局和目标布局,移动棋子从初始布局到达目标布局,求解移动步骤并输出。请设计算法,使用合适的搜索策略,在较少的空间和时间代价下找到最短路径。
二. 开发环境
- 实现语言:Java
- 开发平台:IntelliJ IDEA
三. 项目设计
(1)A*算法
这是一种在静态路网中求解最短路径的有效算法,通俗地讲,它不是像深度优先搜索算法和广度优先搜索算法一样的傻瓜式的埋头搜索,它是先对当前的情况进行分析,得到最有可能的一个分支,然后在该分支上进行扩展,然后将扩展的结果放在之前的大环境中进行比较,再选取最有可能的分支进行扩展,直到找到最终状态。
A*算法的核心是估价函数的选取(通俗的说就是对当前情况的评价方式的选取,通过什么方式选取的分支才是最有可能离最终状态最近的分支)。
公式表示为: f(n)=g(n)+h(n),
其中 f(n) 是从初始点经由节点n到目标点的估价函数,
g(n) 是在状态空间中从初始节点到n节点的实际代价,
h(n) 是从n到目标节点最佳路径的估计代价。
最佳路径查找步骤:
1,从起始状态A开始,并且把它作为待处理点存入一个“open list”。open list就像一张购物清单。尽管现在列表里只有一个元素,但以后就会多起来。你的路径可能会通过它包含的其他状态,也可能不会。基本上,这是一个待检查方格的列表。
2,寻找起点周围通过上下左右移动所有可到达的状态。也把他们加入open list。为所有这些状态保存状态A作为“父方格”。当我们想描述路径的时候,父方格的资料是十分重要的。后面会解释它的具体用途。
3,从open list中删除状态A,把它加入到一个“close list”。
4,取出open list中 f(n)最低的状态,判断该状态性质
(1) 如果在open list中出现,那么判断该状态的 g(n) 通过当前节点(从open list中取出,扩展出该节点的节点)作为父节点带来的 g(n) 是否小于原先的父节点,若小于则需要更改它的父节点以及 g(n) .
(2) 如果在close list中出现,那么判断该状态的 g(n) 通过当前节点(从open list中取出,扩展出该节点的节点)作为父节点带来的 g(n) 是否小于原先的父节点,若小于则需要从close list中删除该节点,并且将其加入open list,并更改其父节点为当前节点。
(3) 既不在open list,也不在close list,这样就很简单,直接加入open list中就行(保持open list升序排列)
(4) 如果取出的节点为目标节点,则成功退出
A*算法的伪代码
创建两个表,open表保存所有已生成而未考察的节点,close表中记录已访问过的节点。
算起点的估价值;
将起点放入open表;
while(open!=NULL)
{
从open表中取估价值f最小的节点n;
if(n节点==目标节点){
break;
}
for(当前节点n 的每个子节点X)
{
算X的估价值;
if(X in open)
{
if( X的估价值小于open表的X估价值 ){
把n设置为X的父亲;
更新open表中的估价值; //取最小路径的估价值
}
}
if(X in close) {
if( X的估价值小于close表的X估价值 )
把n设置为X的父亲;
将该节点从close表中除去
把X节点放入open //取最小路径的估价值
}
}
if(X not in both){
把n设置为X的父亲;
求X的估价值;
并将X插入open表中; //升序排列open
}
}//end for
将n节点插入close表中;
按照估价值将open表中的节点排序; //实际上是比较open表内节点f的大小,从最小路径的节点向下进行。
} //end while(open!=NULL)
保存路径,即从终点开始,每个节点沿着父节点移动直至起点,这就是你的路径;
(2)A*算法解决九宫格重排问题
算法计算公式 f(n) = g(n)+h(n)
其中 g(n) 为从起始状态到当前状态所消耗的步数,h(n)为估算从当前状态到目标状态所需的步数,一般h(n)小于等于实际需要步数为好,这样不会将最优解忽略,因为h(n)和解空间有一些关系,如果h(n)设置的比实际需要的步数多,那么解空间就有可能将最优解忽略。举个例子,宽度优先搜索就是h(n)=0带来的效果,深度优先搜索就是g(n)=0带来的效果,不过h(n)距离h*(n)[实际需要的步数]的程度不能过大,否则h(x)就没有过强的区分能力,算法效率并不会很高。对一个好的h(n)的评价是:h(n)在h*(n)的下界之下,并且尽量接近h*(n)。
那么八数码问题 g(n) 为经过上下左右移动空格附近的数字来得到新状态所需步数,h(n) 为当前状态与目标状态的距离,就是所有不在目标位置的数字总和,必然小于h*(n)。
对于八数码问题,每个结点有8个数字和一个空格,可以将空格看成0,那么一共有9个数字,可以用一个整数表示一个结点对应的信息。
我们令g(n)为从开始节点到当前节点所经过的实际步数(即深度),h(n)为从该节点到最终节点必须至少要的步数的估计(在估算h(n)时,我们忽略其他的数字对该数字到其最终位置的影响,所以h(n)只是其最小步数的下限),f(n)=h(n)+g(n)。