基于Python实现的AStar求解八数码问题
资源下载地址:https://download.csdn.net/download/sheziqiong/86776612
资源下载地址:https://download.csdn.net/download/sheziqiong/86776612
AStar求解八数码问题
一、N数码问题
1.1 N数码问题简介
N数码问题又称为重拍拼图游戏,以8数码为例:在 3X3 的方格棋盘上,放置8个标有1、2、3、4、5、6、7、8数字的方块和1个空白块,空白块可以上下左右移动,游戏要求通过反复移动空白格,寻找一条从某初始状态到目标状态的移动路径。
令W,HW, HW,H为棋盘的宽高,有N=W×H−1N = W \times H - 1N=W×H−1,维度越高求解难度也就越高。
N数码问题的求解策略有广度优先搜索和启发式搜索等,启发式搜索能够利用N数码信息,加快搜索速度。使用广度优先搜索和A*搜索算法都可以求得最优解,即总开始状态到结束状态的最短路径。
1.2 N数码问题的不可解情况
首先给出逆序数的定义:对于串中相邻的一对数,如果位置较前的数大于位置靠后的数,则成为一对逆序,一个串的逆序总数成为逆序数。例如 214322143221432 的逆序数为3,逆序对包括 212121,434343,323232
在N数码中,并不是所有状态都是可解的。将一个状态表示为一维数组的形式,计算除了空位置(0)之外的所有数字的逆序数之和,如果初始状态和结束状态的逆序奇数的偶性相同,则相互可达,否则相互不可达。
简单证明上述结论的必要性:当空块左右移动是不改变逆序数,上下移动时,相当于一个非空块向前或向后移动2格子,如果跨过的两个数字都大于/小于移动的数字,则逆序数可能增减2,如果两个数字一个大一个小,逆序数不变。
因此在设计测试样例时需要考虑逆序数为偶数,否则程序不会终止。例如:[1,2,3,4,5,6,8,7,0] 就是一个不可解情况。
二、A*搜索算法
2.1 基本思想
A*搜索算法利用启发性信息来引导搜索,动态地确定搜索节点的排序,每次选择最优的节点往下搜索,可以高效地减少搜索范围,减少求解的问题的时间。
A*算法用估价函数来衡量一个节点的优先度,优先度越小的节点越应该被优先访问。估价函数用如下公式表示:f(n)=h(n)+d(n)f(n) = h(n) + d(n)f(n)=h(n)+d(n),h(n)h(n)h(n)是对于当前状态的估计值,例如曼哈顿距离,d(h)d(h)d(h)是已经付出的搜索代价,例如节点的深度。
A*算法和A搜索算法的不同之处在于,A*保证对于所有节点都有:h(n)≤h∗(n)h(n) \le h^*(n)h(n)≤h∗(n),其中h∗(n)h^*(n)h∗(n)为当前状态到目的状态的最优路径的代价。
在本次实验中,我们选择曼哈顿距离为h(n)h(n)h(n),节点深度为d(n)d(n)d(n),在实际考虑中:所有放错位的数码个数、后续节点不正确的数码个数都可以作为估价方法,也可以选择多个估价方法并给予适当权重来加快搜索速度。
2.2 算法步骤
- 将起始节点放入 explore_list 中
- 如果 explore_list 为空,则搜索失败,问题无解,否则循环求解
- 取出 explore_list 的估价函数最小的节点,置为当前节点 current_state,若 current_state 为目标节点,则搜索成功,计算解路径,退出
- 寻找所有与 current_state 邻接且未曾被发现的节点,插入到 explore_list 中,并加入 visited_list 中,表示已发现
三、原程序说明
原程序在搜索基础上使用了估价函数来进行优化,但无法求得最优解。原程序在每一个状态的下一个可行状态中选择评估函数最小的一个状态作为下一步,搜索的节点为链状结构(可能有环,而一般的A*搜索结果是树状结构)。
程序步骤:
- 创建 openTable(队列结构) 和 closeTable,并将初始节点加入 openTable
- 如果 openTable 为空,则结束程序,问题无解,否则循环求解
- 从 openTable Pop出一个状态并将其加入到 closeTable 中
- 确定下一步的节点:从当前可行状态中寻找下一步可行状态,选择估价值最小的一个加入到 openTable 中
- 判断当前状态和终止状态是否相等,若相等则结束程序输出路径
原程序没有充分利用 closeTable,因此可能会出现死循环(未证明):
- 一个状态在选择它的下一个状态时不会选择父亲状态,但是有可能选择父亲状态的父亲状态(即爷爷状态),导致有可能出现一个长度至少为3的回路。
要修改程序,必须修改2-1、2-2 步骤
- 选取的新状态应该是 openTable 估价值最小的状态,
- 应该将当前所有可行状态加入到 openTable 中,而不是仅加入估价值最小的状态,
- 应充分利用 closeTable,状态不能重复出现
四、程序修改
我们在源程序的基础上修改程序,使其能够实现最优求解。
正确的A*搜索算法步骤:
- 将起始节点放入 explore_list 中
- 如果 explore_list 为空,则搜索失败,问题无解,否则循环求解
- 取出 explore_list 的估价函数最小的节点,置为当前节点 current_state
- 若 current_state 为目标节点,则搜索成功,计算解路径,退出
- 寻找所有与 current_state 邻接且未曾被发现的节点,插入到 explore_list 中,并加入 visited_list 中,表示已发现
4.1 程序修改
State类:表示一个状态
- 在源代码的基础上,做了以下修改:
- 删除
def solve(self)
方法:将搜索职责转移到Solution类上,State类只负责管理当前的状态。 - 增加属性值
self.d
:状态的深度,并在nextStep()
方法中维护,起始状态的深度为0,终点状态的深度为最右搜索路径长度。 - 修改
getFunctionValue(self)
方法:返回值为哈夫曼距离和状态深度的和,这是一个满足A*搜索的评估函数。
- 删除
- 在源代码的基础上,做了以下修改:
Solution类:负责求解N数码问题
- 记录起始状态、结束状态、当前状态和搜索结点总数,并用三个链表记录待检查状态链表、已检查状态链表和最优路径。
getBestState
方法:遍历待检查状态链表 explore_list,返回估价函数最小的状态isVisited
方法:遍历已检查状态链表 visited_list, 返回 True 如果该状态已经存在于 visited_list 中getPath
方法:获取最终解路径,保存在 path_list 中AStarSolve
方法:使用A*搜索策略求解问题BFSSolve
方法:使用BFS策略求解问题,用于测试A*算法是否正确
4.2 复杂度分析
由于我们使用普通的链表来记录记录待检查状态链表(explore_list
)、已检查状态链表(visited_list
)和最优路径(path_list
):
- 每次获取估价函数最小的状态时需要遍历
explore_list
,时间复杂度为O(n)O(n)O(n) - 每次判断一个状态是否已经被搜索过时需要遍历
visited_list
,时间复杂度为O(n)O(n)O(n)
设最终所搜的节点数目为MMM,有复杂度为O(M2)O(M^2)O(M2)
4.3 代码优化
在修改程序的基础上对待检查状态链表(explore_list
)和已检查状态链表(visited_list
)进行优化:
- 考虑它们的特性,用一个优先队列来实现
explore_list
,用一个哈希表(字典)来实现visited_list
,可以将每次获取估价函数最小的状态和每次判断一个状态是否已经被搜索过的复杂度都降为O(1)O(1)O(1) - 而
explore_list
插入节点并维护优先队列结构的复杂度为O(log(n))O(\log(n))O(log(n))
从而让整个算法复杂度降低到O(Mlog(M))O(M\log(M))O(Mlog(M)),从下面的测试中可以看到优化的运行速度优于优化前的运行速度。
最后,可以通过优化估价函数来缩短搜索次数,例如使用错误码数、曼哈顿距离的加权。
五、测试结果
5.1 使用BFS验证A*算法正确性
对于8数码测试样例:
begin = State(np.array([[1, 5, 2],[7, 0, 4],[6, 3, 8]])) end = State(np.array([[1, 2, 3],[4, 5, 6],[7, 8, 0]]))
原程序测试结果:(非最优路径)
... Total steps is 28
BFS结果:耗时长,访问节点数多,求得最短路径及其长度 14 。
... Total search node is 5905 Total steps is 14 Totally cost is 105.18757700920105 s
优化前的结果:正确求得最短路径,耗时0.02秒
... Total search node is 53 Total steps is 14 Totally cost is 0.020945310592651367 s
优化后的结果:正确求得最短路径,耗时0.01秒
... Total search node is 52 Total steps is 14 Totally cost is 0.010965108871459961 s
5.2 优化前后效率对比
对于下面八数码测试样例:
begin = State(np.array([[1, 3, 2],[4, 5, 6],[8, 7, 0]])) end = State(np.array([[1, 2, 3],[4, 5, 6],[7, 8, 0]]))
优化前的结果:需要 9 秒才能求出最优解
... Total search node is 1664 Total steps is 20 Totally cost is 9.302738666534424 s
优化后的结果:只需要 0.6 秒就能求出最优解
... Total search node is 2730 Total steps is 20 Totally cost is 0.641481876373291 s
5.3 24数码问题求解测试
对于以下24数码的测试样例:
begin = State(np.array([[ 1, 2, 3, 4, 5],[ 6, 12, 8, 9, 10],[11, 7, 13, 14, 15],[16, 19, 18, 17, 20],[21, 22, 23, 24, 0]]))end = State(np.array([[ 1, 2, 3, 4, 5],[ 6, 7, 8, 9, 10],[11, 12, 13, 14, 15],[16, 17, 18, 19, 20],[21, 22, 23, 24, 0]]))
AStarSearchOptimized 运行输出:求得最短路径及其长度为26,此时非优化A*与BFS无法在可接受时间内完成求解。
... -> 26: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 0 24 -> 27: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 0 -> Total search node is 91640 Total steps is 26 Totally cost is 36.743870973587036 s
资源下载地址:https://download.csdn.net/download/sheziqiong/86776612
资源下载地址:https://download.csdn.net/download/sheziqiong/86776612
基于Python实现的AStar求解八数码问题相关推荐
- 利用Python求解八数码难题
实验目的 实验内容 八数码问题也称为九宫问题.在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同.棋盘上还有一个空格,与空格相邻的棋子可以移到空格中.要求解决的问题 ...
- Python利用A*算法解决八数码问题
资源下载地址:https://download.csdn.net/download/sheziqiong/86790565 资源下载地址:https://download.csdn.net/downl ...
- 多种方法求解八数码问题
AI的实验报告,改了改发上来.希望路过的大牛不吝赐教.非常是纳闷我的ida*怎么还没有双搜快.还有发现基于不在位启示的A*和Ida*都挺慢.尤其是ida* 搜索31步的竟然要十几秒.是我写的代码有问题 ...
- 人工智能作业 - A*算法程序求解八数码
文章目录 A*算法 简介 思路 A*伪代码 八数码问题 题目描述 注意事项 实验过程 解决思路 伪代码 二维压缩为一维 检查是否有解 其他 代码实现 h1和h2的对比 关于曼哈顿距离 参考链接 A*算 ...
- 7种方法求解八数码问题
[八数码问题]//https://vijos.org/p/1360 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要 ...
- 人工智能:(C语言)采用状态空间法求解八数码问题
实验要求:八数码难题也称九宫问题,它是在3×3的方格棋盘上,分别放置了表有数字1.2.3.4.5.6.7.8的八张牌,初始状态S0,目标状态Sg,要求程序能输入任意的初始状态和目标状态,要求通过空格来 ...
- 使用Muduo完成数独和八数码问题求解服务器
在剖析完Muduo网络库源码之后,我们试着完成一个高效的数独和八数码问题求解服务器. 先说说为什么要选择这两个问题?数独问题一直是陈硕老师很喜欢的问题,在muduo网络库中多次提到并有示例.八数码问题 ...
- 广州大学人工智能导论实验一(八数码问题)
实验一 八数码问题 一. 实验目的 该课程的教学应贯彻理论与实践相结合的原则,为学生所学到的理论提供实践的场所,通过实验课程中具体问题的求解达到深入了解并掌握的目的. 二. 实验环境 微型计算机,操作 ...
- 【人工智能】八数码问题:广度搜索、深度搜索
应用广度搜索BFS和深度搜索DFS解决八数码问题,广度搜索和深度搜索都是盲目搜索,相关理论知识,算法过程:问题求解:状态空间图和盲目搜索. 参考:7种方法求解八数码问题 Python实现A*算法解决N ...
最新文章
- ios转向前端进阶之:html标签类型
- centos修改SSH端口并禁用root远程登录
- 【软件开发底层知识修炼】十二 C/C++语言中内嵌汇编语言(asm)
- 关于Win2003下ASP无故停止
- 哪几所大学计算机软件方面是强项,计算机软件工程专业排名靠前的大学是那几所...
- day12-html(css)
- [置顶] C#中给Label控件设置BackgroundImage属性
- 微信支付开发文档说明
- Gradle下载官方地址
- 使用nssm注册 windows服务
- 深入浅出PID控制算法(二)——PID算法离散化和增量式PID算法原理及Matlab实现
- CC1310开发笔记
- vmware虚拟机连接服务器超时,vmware连接远程服务器超时
- REmap包介绍及使用
- python unpacking_Python函数调用时unpacking参数特性
- 创维E900V21E机顶盒刷机避坑
- Geohash算法的概括
- python怎样画动态文字_Python之pygame学习绘制文字制作滚动文字
- [论文阅读笔记45]ChineseBLUE[MC-BERT]
- php 实线,PHP实现的功能是显示8条基色色带