收藏 分享(赏)

基于启发式搜索算法A星解决八数码问题.pdf

上传人:精品资料 文档编号:10346961 上传时间:2019-11-02 格式:PDF 页数:11 大小:294.51KB
下载 相关 举报
基于启发式搜索算法A星解决八数码问题.pdf_第1页
第1页 / 共11页
基于启发式搜索算法A星解决八数码问题.pdf_第2页
第2页 / 共11页
基于启发式搜索算法A星解决八数码问题.pdf_第3页
第3页 / 共11页
基于启发式搜索算法A星解决八数码问题.pdf_第4页
第4页 / 共11页
基于启发式搜索算法A星解决八数码问题.pdf_第5页
第5页 / 共11页
点击查看更多>>
资源描述

1、人工智能实验报告 基于启 发式搜 索算法 A* 问题 求 解方法 实验 姓名:王鑫 学号:12349021 日期 :2016.1.3 摘要 本篇报告的所介绍的内容是使用 A*算法解决八数码问题。 包括介绍 A*算法的流程, 如 何使用 A*算法来解决八数码问题, 本文依照 A*算法的流程, 详细的介绍了 A*算法的每一步 的实现方法。 最后输入数据对 A* 算法的性能作出评估,通 过 与 BFS 算法的对比, 总结了 A* 算法的优势体现在哪里,并且反思了 A*算法存在的不足。 1 导言 本次试验我准备使用 A*算法解决八数码问题。 八数码问题也称为九宫问题,是在 33 的棋盘,摆有八个棋子,

2、每个棋子上标有 1 至 8 的某一数字 , 不同棋子上标的数字不相同。 棋盘上还有一个空格, 与空格相邻的棋子可以 移到空格中。 要求解决的问题是: 给出一个初始状态和一个目标状态, 找出一种从初始转变 成目标状态的移动棋子步数最少的移动步骤。 所谓问题的 一个状态就 是棋子在棋 盘上的一种 摆法。棋子 移动后,状 态就会发生 改变 。 解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态。 八数码问题一般使用搜索法来解。 广度优先搜索是解决八数码问题常用的一种方法, 但 是使用广度优先搜索有很大的缺陷, 虽然广度优先搜索法在有解的情形总能保证搜索到最短 路经, 也就是移

3、动最少步数的路径, 但广度优先搜索法的最大问题在于搜索的结点数量太多。 因为在广度优先搜索法中, 每一个可能扩展出的结点都是搜索的对象。 随着结点在搜索树上 的深度增大, 搜索的结点数会很快增长, 并以指数形式扩张, 从而所需的存储空间和搜索花 费的时间也会成倍增长。 所以本次实验我选用 A*算法来解决八数码问题,A* 算法是一种启发式的搜索算法,与 1 属于盲搜索算法的广度优先算法不同的是,A*算法从 open 表中选取的是启发式函数值最优 的节点来生成后继节点。所以 A*算 法可以大大减少搜索无关节点的数目,从而提高搜索效 率。 2 实验过 程 2.1 A*算法的流 程 (1 ) 定义两

4、个表, 一个是 open 表, 用来存放已经生成, 并且已用启发式函数做过估计或评 价, 但尚未产生他们的后继节点的那些节点。 另一个是 close 表, 用来存放, 已经生成,并 且已经用启发式函数做过估计且已经产生后继节点的那些节点。 (2 )初始化两张表,将所有初始状态存放进 open 表中,将 close 表清空。 (3 )如果 open 表为空,失败退出 (4 )在 open 表中取出启发式函数 ( ) 值最小的节点 n ,把 n 放入 close 表中。 (5 )如果n (目标状态),则成功退出,此时解为 Tree 上从 n 到 0 (初始状态)的路径。 (6 ) 产生 n 的一切

5、后继, 将后继中不是 n 的前 驱点的一切点构成集合 M , 将装入 G 作为 n 的后继, 这就除掉了既是 n 的前驱又是 n 的后继 的节点, 就避免了回路, 节点之间有偏序关 系存在。 (7 )对 M 中的任意一个元素 P ,分别作两类处理: a. 若P G,即 P 不 在 open 表中,也不在 close 表中,则 P 根据一定原则加入到 open 表中, 同时加入搜索图 G 中,对 P 进行估计放入 Tree 中。 b. 若P G,则决定是否更改 Tree 中 P 到 n 的 指针。 (8 )转第(3 )步。 2.2 A*算法解决 八数码 问题的 步骤 2.2.1 定义 节点类型

6、在 A*算法中我们会用到一张 open 表, 一 张 close 表,一棵搜索树,以及以个搜索图。 这些数据结构中都是由节点彼此链接构成的。所以我们定义如下的节点结类型: 2 其包含一个二维数组 statue 来记录当前节点的状态; 包含多个指针, 这些指针的作用是把同一个节点链接在不同的数据结构中。 例如,指 针 Tparent 的作用是把该节点链接到搜索树中。在 该节点被接入搜索树中时对 Tparent 赋值 , 使 得 Tparent 指向该节点在树中的前驱节点,如果该节点尚未被接入搜索树,则 Tparent 的值 为空。 同理,如果该节点在 open 表中,则 opennext 指向

7、该节点在 open 表中的下一个节点 。 其余指针类似也是类似的道理。 / 定义算法中 用到的链表,图,树的节点的结构。 struct Node int statuesizesize; /记 录当前节点的状态 struct Node * Tparent; / 用来构成搜索树,该树由搜索图的反向指针构成 struct Node * opennext; / 用来构成 open 表, 该指 针指向该节点在 open 表中的下一个 节点 struct Node * closenext; / 用来构成 open 表,该指针指向该节点在 close 表中 的下一个 节点 struct Node * bro

8、thernext; / 构成兄弟链表, 该指针 指向该节点在兄弟链表中的下一个节 点 int f; / 记录 当前节点的 f 函数值 int g; / 记录 当前节点的 g 函数的值 int h; / 记录 当前节点的 h 函数的值 ; 定义表头类型, 用来定义表头变量。 例如, 定义一个变量 head_o 来作为 open 表的表头,指 向 open 表的第一个节点。 /定义表头结 构 struct head struct Node * next; ; 3 2.2.2 定义 启发式 函数 试验中选择的启发式函数是: ( ) = ( ) + ( ) 其中 ( ) 表示节点 n 到搜索 树根节点

9、的距离。 ( ) 是估计的放错位置的数字的个数。 可以看出( ) 的值必定小于等于实际的从当前节点到 达目标节点的最小耗费 /得到当前节 点的 f 值 void get_f (Node * n) int fvalue; / 计算 h(n); int hvalue=0; for (int i=0; istatueij!=statuegij) hvalue+; n-h=hvalue; n-f=hvalue+n-g; 2.2.3 定义 两张表 和 搜索树 并 初始化 如下所示,我们直接定义 head 类型 的三个变量,head_o 作为 open 表的表头;head_c 作为 close 表的表头;

10、Troot 的 next 属性 指向搜索树的根。 head head_o; head head_c; head Troot; 初始化 open 表, 将初始状态 S0 接入 open 表中; 初始化 close 表为空; 初始化搜索树, 将 S0 作为搜索树的根节点。 / 初始化两张 表, 和树 head_o.next=S0; 4 head_c.next=NULL; Troot.next=S0; 2.2.4 判断 open 表是否为 空 如果 open 表为空,则搜索失败,退出。 if (head_o.next=NULL) cout “查找 失败!“endl; return; 2.2.5 在

11、open 表中 取出 最 优 节点 我们定义一个指针 bestNode 。利用 get_bestNode 函数,让 bestNode 指向 open 表中启 发式函数值最优的节点。 并将该节点的 opennext 属性置空,即 从 open 表中移除该节点。 将 该节点利用 move_to_close 函数添加进 close 表中。 Node* bestNode; / 定义 f 值最小的节点 bestNode=get_bestNode( /选择 f 值最小的节点 bestNode ,并从 open 表中移 除该节点 move_to_close( / 将 bestNode 添加进 close 表

12、中。 2.2.6 判断 bestNode 所指节 点是不是 属于目标 状态 使用 ifequal 函数判断 bestNode 是不是属于目标状态。 如果属于 , 就成功找到 目标状态, 利用 get_bestroute 函数输出最佳路径并退出。 if (bool equal=ifequal (bestNode,Sg) cout “找到 目标状态!“endl; cout “请按 任意键,输出最佳路径.“endl; system(“pause“); 5 get_bestroute (bestNode); return; 2.2.7 生成 bestNode 所指节 点的后继 节点 定义一个后继节点链

13、表, 表头为 head_b,将 bestNode 所指节点的不是前驱节点的后继 节点,链接到后继及诶单链表中。getchild 函数可以实现这个功能。 /产生 bestNode 的一切后继节点。 。 head head_b; / 定义 bestNode 的后继节点表 head_b.next=NULL; getchild ( / 产生 bestNode 的子节点,将不是 bestNode 的父节点的 子节点放入后继节点表中 2.2.8 对 后继节点 链表中的 节点 分 类 处理 利用 getbrother 函数从后继节点链表中取得一个节点,并将该节点从后继节点中移除。 对该节点依照条件,作下面的

14、处理: 利用 atopen 函数判断该节点是否在 open 表中,如果在,则比较该节点与 open 表中状态相 同的节点到搜索树根的距离, 即g( ) 的值, 如果前者小于后者, 则更新搜索树, 并用前者 替 换掉后者。 利用 atclose 函数判断该节点时候在 close 表中, 如果在,则比较该节点与 close 表中 状 态相同的节点到搜索树根的距离, 即g( ) 的值, 如果前者小于后者, 则更新搜索树, 以及 用 前者替换掉后者。 如果上面的两种条件都不满足, 即该节点之前在搜索图中没有出现过, 则将该节点连接 到搜索树中,并用 move_to_open 函数将该节点连接到 ope

15、n 表尾部。 while (head_b.next!=NULL) Node *tmp=getbrother ( / 从 后继节点表中取出一个节点记为 tmp,并从 6 后继节点表中删除该节点 Node * atposion; / 如果在 open 表中或在 close 表中, 则返回 true,并 将 atposion 指 向该节点 if (bool at1=atopen( atposion-f=tmp-f; atposion-g=tmp-g; atposion-h=tmp-h; else if (bool at2=atclose( atposion-f=tmp-f; atposion-g=t

16、mp-g; atposion-h=tmp-h; else tmp-Tparent=bestNode; move_to_open( continue; 2.2.9 转到 2.2.4 继续执行 。 3 结果分 析 3.1 程序运行 情况 示例 实验在 DEVC+ 平台上进行,采用 C+ 语言实现算法。实验的完整代码可参见文件 myA_star.cpp 。 实验的输入数据为:初始状态和目标状态,其中以 0 代替 八数码中的空格。 实验的输出可由用户进行选择,以输入初始状态 2 8 3 1 6 4 7 0 5,输出状态 1 2 3 8 0 4 7 6 5为例: 选择手动搜索, 则我们可以看到 A*算法

17、的决策过程, 即每一次从 open 表中拿出的节点, 以 7 及该节点生成的最优节点的非前驱的后继节点都是什么。 部分截图如下所示: 选择自动搜索, 则程序自动运行, 直到找到目标状态才会暂停, 用户按任意键便会生成从初 始状态到目标状态的最佳路径。 部分截图如下图所示: 8 3.2 算法性能分 析 利用 time.h 头文件中的 clock()函数进行计时, 输入一些数据进行运行测试。计 时 范围为开始 搜索,到找到目标状态。 下面输入的目标状态均为: 1 2 3 8 0 4 7 6 5对于输入 7 2 3 8 1 0 6 5 4 ,从初始状态到目标状态需要 13 步, 搜索到最佳路径用时

18、0S 。 对于输入 4 2 5 7 0 1 3 8 6 ,从初始状态到目标状态需要 20 步, 搜索到最佳路径用时 0S 。 9 对于输入 8 7 3 1 6 0 5 4 2 ,从初始状态到目标状态需要 21 步, 搜索到最佳路径用时 1S 。 对于输入 1 5 6 2 7 4 0 3 8 ,从初始状态到目标状态需要 22 步, 搜索到最佳路径用时 3S 。 可以看出当从初始状态到目标状态所用步数增加时, 搜索用时会增加。 当步数越大时, 步数 的增加会造成搜索用时急剧上升。 下面是一张输入为 7 2 3 8 1 0 6 5 4 时的算法性能分析表: 3.3 与 其他算法 对比 对比 BFS

19、算法, 可以看出 在初始状态到目标状态的步数在 5 步以内时, BFS 与 A*算法区 别不大, 但是当状态空间比较大时, BFS 算法与 A*算法的性能对比便很明显了。 在初始状态 到目标状态的步数超过 16 步之后,BFS 的运行时间便会非常长,相较而言,在初始状态到 目标状态的步数为 22 步时 ,A*算法只需要 3S 便 可搜索到最优路径,这种情况下 A*算法的 优势便很明显了。 4 结论 本文介绍了使用 A*算法解决八数码问题, 括介绍 A*算法的流程, 如何使用 A*算法来解 决八数码问题, 本文依照 A*算法的流程, 详细的介绍了 A*算法的每一步的实现方法。最 后 输入数据对

20、A* 算法的性能作出评估, 并将 A*算法与 BFS 算法做了对比。 通过上面的描述我 10 们可以看出 A* 算法作为一种启发式搜索算法,它在启发式函数选择合适的情况下,能够极 大地减少搜索节点的数目,对程序性能的提高起到了至关重要的作用。但是 A*算法 也存在 这自己的一些缺点, 比如当启发式函数选择不恰当时, 算法的性能就会大打折扣。 另外当搜 索树的深度比很大时,使用 A*算法和 BFS 算法类似,同样是很耗时的。 主要参 考文献 1 朱福 喜. 人工智能导论. 清华大学出版社,2011 年. 2 阿凡 卢 八数码问题及 A*算法. 2012 年 3 王 文杰, 史忠植.2007. 人工智能原理辅导与练习. 北京:清华大学出版社出 4 王 勋,凌 云,费玉莲.2005. 人工智能 导论. 北京:科学出版社 11

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报