基于启发式搜索算法A星解决八数码问题
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
5
get_bestroute (bestNode); return; }
2.2.7 生成 bestNode 所指节点的后继节点
定义一个后继节点链表,表头为 head_b,将 bestNode 所指节点的不是前驱节点的后继 节点,链接到后继及诶单链表中。getchild 函数可以实现这个功能。
//产生 bestNode 的一切后继节点。。 head head_b; //定义 bestNode 的后继节点表 head_b.next=NULL; getchild (&head_b,bestNode); //产生 bestNode 的子节点,将不是 bestNode 的父节点的
if (bool at1=atopen(&head_o,tmp,&atposion)) { if (atposion->g > tmp->g) { atposion->Tparent=tmp->Tparent; atposion->f=tmp->f; atposion->g=tmp->g; atposion->h=tmp->h; }
1
属于盲搜索算法的广度优先算法不同的是,A*算法从 open 表中选取的是启发式函数值最优 的节点来生成后继节点。所以 A*算法可以大大减少搜索无关节点的数目,从而提高搜索效 率。
2 实验过程
2.1 A*算法的流程
(1)定义两个表,一个是 open 表,用来存放已经生成,并且已用启发式函数做过估计或评 价,但尚未产生他们的后继节点的那些节点。另一个是 close 表,用来存放,已经生成,并 且已经用启发式函数做过估计且已经产生后继节点的那些节点。 (2)初始化两张表,将所有初始状态存放进 open 表中,将 close 表清空。 (3)如果 open 表为空,失败退出 (4)在 open 表中取出启发式函数������������(������������)值最小的节点 n,把 n 放入 close 表中。 (5)如果n ∈ ������������������������(目标状态),则成功退出,此时解为 Tree 上从 n 到������������0(初始状态)的路径。 (6)产生 n 的一切后继,将后继中不是 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.3 定义两张表和搜索树并初始化
如下所示,我们直接定义 head 类型的三个变量,head_o 作为 open 表的表头;head_c 作为 close 表的表头;Troot 的 next 属性指向搜索树的根。
head head_o; head head_c; head Troot; 初始化 open 表,将初始状态 S0 接入 open 表中;初始化 close 表为空;初始化搜索树, 将 S0 作为搜索树的根节点。 //初始化两张表,和树 head_o.next=S0;
我们定义一个指针 bestபைடு நூலகம்ode。利用 get_bestNode 函数,让 bestNode 指向 open 表中启 发式函数值最优的节点。并将该节点的 opennext 属性置空,即从 open 表中移除该节点。将 该节点利用 move_to_close 函数添加进 close 表中。
Node* bestNode; //定义 f 值最小的节点 bestNode=get_bestNode(&head_o); //选择 f 值最小的节点 bestNode,并从 open 表中移 除该节点 move_to_close(&head_c,bestNode); //将 bestNode 添加进 close 表中。
1 导言
本次试验我准备使用 A*算法解决八数码问题。 八数码问题也称为九宫问题,是在 3×3 的棋盘,摆有八个棋子,每个棋子上标有 1 至 8 的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以 移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变 成目标状态的移动棋子步数最少的移动步骤。 所谓问题的一个状态就是棋子在棋盘上的一种摆法。棋子移动后,状态就会发生改变。 解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态。 八数码问题一般使用搜索法来解。广度优先搜索是解决八数码问题常用的一种方法,但 是使用广度优先搜索有很大的缺陷,虽然广度优先搜索法在有解的情形总能保证搜索到最短 路经,也就是移动最少步数的路径,但广度优先搜索法的最大问题在于搜索的结点数量太多。 因为在广度优先搜索法中,每一个可能扩展出的结点都是搜索的对象。随着结点在搜索树上 的深度增大,搜索的结点数会很快增长,并以指数形式扩张,从而所需的存储空间和搜索花 费的时间也会成倍增长。 所以本次实验我选用 A*算法来解决八数码问题,A*算法是一种启发式的搜索算法,与
4
head_c.next=NULL; Troot.next=S0;
2.2.4 判断 open 表是否为空
如果 open 表为空,则搜索失败,退出。 if (head_o.next==NULL) {
cout <<"查找失败!"<<endl; return; }
2.2.5 在 open 表中取出最优节点
} } else {
tmp->Tparent=bestNode; move_to_open(&head_o,tmp); continue; } }
2.2.9 转到 2.2.4 继续执行。
3 结果分析
3.1 程序运行情况示例
实 验 在 DEVC++ 平 台 上 进 行 , 采 用 C++ 语 言 实 现 算 法 。 实 验 的 完 整 代 码 可 参 见 文 件
} else if (bool at2=atclose(&head_c,tmp,&atposion)) {
if ((atposion->g) > (tmp->g)) { atposion->Tparent=tmp->Tparent; atposion->f=tmp->f; atposion->g=tmp->g; atposion->h=tmp->h;
利用 atclose 函数判断该节点时候在 close 表中,如果在,则比较该节点与 close 表中状 态相同的节点到搜索树根的距离,即g(������������)的值,如果前者小于后者,则更新搜索树,以及用 前者替换掉后者。
如果上面的两种条件都不满足,即该节点之前在搜索图中没有出现过,则将该节点连接 到搜索树中,并用 move_to_open 函数将该节点连接到 open 表尾部。
定义表头类型,用来定义表头变量。例如,定义一个变量 head_o 来作为 open 表的表头,指 向 open 表的第一个节点。
//定义表头结构 struct head {
struct Node * next; };
3
2.2.2 定义启发式函数
试验中选择的启发式函数是: ������������(������������) = ������������(������������) + ℎ(������������)
int fvalue; //计算 h(n); int hvalue=0; for (int i=0; i<size; ++i) {
for (int j=0; j<size; ++j) { if (n->statue[i][j]!=statueg[i][j]) { hvalue++; }
} } n->h=hvalue; n->f=hvalue+n->g; }
其中������������(������������)表示节点 n 到搜索树根节点的距离。 ℎ(������������)是估计的放错位置的数字的个数。可以看出ℎ(������������)的值必定小于等于实际的从当前节点到 达目标节点的最小耗费
//得到当前节点的 f 值 void get_f (Node * n) {
//定义算法中用到的链表,图,树的节点的结构。 struct Node {
int statue[size][size]; //记录当前节点的状态 struct Node * Tparent; //用来构成搜索树,该树由搜索图的反向指针构成 struct Node * opennext; //用来构成 open 表,该指针指向该节点在 open 表中的下一个 节点 struct Node * closenext; //用来构成 open 表,该指针指向该节点在 close 表中的下一个 节点 struct Node * brothernext; //构成兄弟链表,该指针指向该节点在兄弟链表中的下一个节 点 int f; //记录当前节点的 f 函数值 int g; //记录当前节点的 g 函数的值 int h; //记录当前节点的 h 函数的值 };
2.2 A*算法解决八数码问题的步骤
2.2.1 定义节点类型
在 A*算法中我们会用到一张 open 表,一张 close 表,一棵搜索树,以及以个搜索图。 这些数据结构中都是由节点彼此链接构成的。所以我们定义如下的节点结类型:
2
其包含一个二维数组 statue 来记录当前节点的状态; 包含多个指针,这些指针的作用是把同一个节点链接在不同的数据结构中。例如,指针 Tparent 的作用是把该节点链接到搜索树中。在该节点被接入搜索树中时对 Tparent 赋值,使 得 Tparent 指向该节点在树中的前驱节点,如果该节点尚未被接入搜索树,则 Tparent 的值 为空。 同理,如果该节点在 open 表中,则 opennext 指向该节点在 open 表中的下一个节点。 其余指针类似也是类似的道理。
人工智能实验报告
基于启发式搜索算法 A*问题求解方法实验
姓名:王鑫
学号:12349021
日期:2016.1.3
摘要
本篇报告的所介绍的内容是使用 A*算法解决八数码问题。包括介绍 A*算法的流程,如 何使用 A*算法来解决八数码问题,本文依照 A*算法的流程,详细的介绍了 A*算法的每一步 的实现方法。最后输入数据对 A*算法的性能作出评估,通过与 BFS 算法的对比,总结了 A* 算法的优势体现在哪里,并且反思了 A*算法存在的不足。
子节点放入后继节点表中
2.2.8 对后继节点链表中的节点分类处理
利用 getbrother 函数从后继节点链表中取得一个节点,并将该节点从后继节点中移除。 对该节点依照条件,作下面的处理: 利用 atopen 函数判断该节点是否在 open 表中,如果在,则比较该节点与 open 表中状态相 同的节点到搜索树根的距离,即g(������������)的值,如果前者小于后者,则更新搜索树,并用前者替 换掉后者。
while (head_b.next!=NULL) { Node *tmp=getbrother (&head_b); //从后继节点表中取出一个节点记为 tmp,并从
6
后继节点表中删除该节点
Node * atposion; //如果在 open 表中或在 close 表中,则返回 true,并将 atposion 指 向该节点
2.2.6 判断 bestNode 所指节点是不是属于目标状态
使用 ifequal 函数判断 bestNode 是不是属于目标状态。如果属于,就成功找到目标状态, 利用 get_bestroute 函数输出最佳路径并退出。
if (bool equal=ifequal (bestNode,Sg)) { cout <<"找到目标状态!"<<endl; cout <<"请按任意键,输出最佳路径..."<<endl; system("pause");
get_bestroute (bestNode); return; }
2.2.7 生成 bestNode 所指节点的后继节点
定义一个后继节点链表,表头为 head_b,将 bestNode 所指节点的不是前驱节点的后继 节点,链接到后继及诶单链表中。getchild 函数可以实现这个功能。
//产生 bestNode 的一切后继节点。。 head head_b; //定义 bestNode 的后继节点表 head_b.next=NULL; getchild (&head_b,bestNode); //产生 bestNode 的子节点,将不是 bestNode 的父节点的
if (bool at1=atopen(&head_o,tmp,&atposion)) { if (atposion->g > tmp->g) { atposion->Tparent=tmp->Tparent; atposion->f=tmp->f; atposion->g=tmp->g; atposion->h=tmp->h; }
1
属于盲搜索算法的广度优先算法不同的是,A*算法从 open 表中选取的是启发式函数值最优 的节点来生成后继节点。所以 A*算法可以大大减少搜索无关节点的数目,从而提高搜索效 率。
2 实验过程
2.1 A*算法的流程
(1)定义两个表,一个是 open 表,用来存放已经生成,并且已用启发式函数做过估计或评 价,但尚未产生他们的后继节点的那些节点。另一个是 close 表,用来存放,已经生成,并 且已经用启发式函数做过估计且已经产生后继节点的那些节点。 (2)初始化两张表,将所有初始状态存放进 open 表中,将 close 表清空。 (3)如果 open 表为空,失败退出 (4)在 open 表中取出启发式函数������������(������������)值最小的节点 n,把 n 放入 close 表中。 (5)如果n ∈ ������������������������(目标状态),则成功退出,此时解为 Tree 上从 n 到������������0(初始状态)的路径。 (6)产生 n 的一切后继,将后继中不是 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.3 定义两张表和搜索树并初始化
如下所示,我们直接定义 head 类型的三个变量,head_o 作为 open 表的表头;head_c 作为 close 表的表头;Troot 的 next 属性指向搜索树的根。
head head_o; head head_c; head Troot; 初始化 open 表,将初始状态 S0 接入 open 表中;初始化 close 表为空;初始化搜索树, 将 S0 作为搜索树的根节点。 //初始化两张表,和树 head_o.next=S0;
我们定义一个指针 bestபைடு நூலகம்ode。利用 get_bestNode 函数,让 bestNode 指向 open 表中启 发式函数值最优的节点。并将该节点的 opennext 属性置空,即从 open 表中移除该节点。将 该节点利用 move_to_close 函数添加进 close 表中。
Node* bestNode; //定义 f 值最小的节点 bestNode=get_bestNode(&head_o); //选择 f 值最小的节点 bestNode,并从 open 表中移 除该节点 move_to_close(&head_c,bestNode); //将 bestNode 添加进 close 表中。
1 导言
本次试验我准备使用 A*算法解决八数码问题。 八数码问题也称为九宫问题,是在 3×3 的棋盘,摆有八个棋子,每个棋子上标有 1 至 8 的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以 移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变 成目标状态的移动棋子步数最少的移动步骤。 所谓问题的一个状态就是棋子在棋盘上的一种摆法。棋子移动后,状态就会发生改变。 解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态。 八数码问题一般使用搜索法来解。广度优先搜索是解决八数码问题常用的一种方法,但 是使用广度优先搜索有很大的缺陷,虽然广度优先搜索法在有解的情形总能保证搜索到最短 路经,也就是移动最少步数的路径,但广度优先搜索法的最大问题在于搜索的结点数量太多。 因为在广度优先搜索法中,每一个可能扩展出的结点都是搜索的对象。随着结点在搜索树上 的深度增大,搜索的结点数会很快增长,并以指数形式扩张,从而所需的存储空间和搜索花 费的时间也会成倍增长。 所以本次实验我选用 A*算法来解决八数码问题,A*算法是一种启发式的搜索算法,与
4
head_c.next=NULL; Troot.next=S0;
2.2.4 判断 open 表是否为空
如果 open 表为空,则搜索失败,退出。 if (head_o.next==NULL) {
cout <<"查找失败!"<<endl; return; }
2.2.5 在 open 表中取出最优节点
} } else {
tmp->Tparent=bestNode; move_to_open(&head_o,tmp); continue; } }
2.2.9 转到 2.2.4 继续执行。
3 结果分析
3.1 程序运行情况示例
实 验 在 DEVC++ 平 台 上 进 行 , 采 用 C++ 语 言 实 现 算 法 。 实 验 的 完 整 代 码 可 参 见 文 件
} else if (bool at2=atclose(&head_c,tmp,&atposion)) {
if ((atposion->g) > (tmp->g)) { atposion->Tparent=tmp->Tparent; atposion->f=tmp->f; atposion->g=tmp->g; atposion->h=tmp->h;
利用 atclose 函数判断该节点时候在 close 表中,如果在,则比较该节点与 close 表中状 态相同的节点到搜索树根的距离,即g(������������)的值,如果前者小于后者,则更新搜索树,以及用 前者替换掉后者。
如果上面的两种条件都不满足,即该节点之前在搜索图中没有出现过,则将该节点连接 到搜索树中,并用 move_to_open 函数将该节点连接到 open 表尾部。
定义表头类型,用来定义表头变量。例如,定义一个变量 head_o 来作为 open 表的表头,指 向 open 表的第一个节点。
//定义表头结构 struct head {
struct Node * next; };
3
2.2.2 定义启发式函数
试验中选择的启发式函数是: ������������(������������) = ������������(������������) + ℎ(������������)
int fvalue; //计算 h(n); int hvalue=0; for (int i=0; i<size; ++i) {
for (int j=0; j<size; ++j) { if (n->statue[i][j]!=statueg[i][j]) { hvalue++; }
} } n->h=hvalue; n->f=hvalue+n->g; }
其中������������(������������)表示节点 n 到搜索树根节点的距离。 ℎ(������������)是估计的放错位置的数字的个数。可以看出ℎ(������������)的值必定小于等于实际的从当前节点到 达目标节点的最小耗费
//得到当前节点的 f 值 void get_f (Node * n) {
//定义算法中用到的链表,图,树的节点的结构。 struct Node {
int statue[size][size]; //记录当前节点的状态 struct Node * Tparent; //用来构成搜索树,该树由搜索图的反向指针构成 struct Node * opennext; //用来构成 open 表,该指针指向该节点在 open 表中的下一个 节点 struct Node * closenext; //用来构成 open 表,该指针指向该节点在 close 表中的下一个 节点 struct Node * brothernext; //构成兄弟链表,该指针指向该节点在兄弟链表中的下一个节 点 int f; //记录当前节点的 f 函数值 int g; //记录当前节点的 g 函数的值 int h; //记录当前节点的 h 函数的值 };
2.2 A*算法解决八数码问题的步骤
2.2.1 定义节点类型
在 A*算法中我们会用到一张 open 表,一张 close 表,一棵搜索树,以及以个搜索图。 这些数据结构中都是由节点彼此链接构成的。所以我们定义如下的节点结类型:
2
其包含一个二维数组 statue 来记录当前节点的状态; 包含多个指针,这些指针的作用是把同一个节点链接在不同的数据结构中。例如,指针 Tparent 的作用是把该节点链接到搜索树中。在该节点被接入搜索树中时对 Tparent 赋值,使 得 Tparent 指向该节点在树中的前驱节点,如果该节点尚未被接入搜索树,则 Tparent 的值 为空。 同理,如果该节点在 open 表中,则 opennext 指向该节点在 open 表中的下一个节点。 其余指针类似也是类似的道理。
人工智能实验报告
基于启发式搜索算法 A*问题求解方法实验
姓名:王鑫
学号:12349021
日期:2016.1.3
摘要
本篇报告的所介绍的内容是使用 A*算法解决八数码问题。包括介绍 A*算法的流程,如 何使用 A*算法来解决八数码问题,本文依照 A*算法的流程,详细的介绍了 A*算法的每一步 的实现方法。最后输入数据对 A*算法的性能作出评估,通过与 BFS 算法的对比,总结了 A* 算法的优势体现在哪里,并且反思了 A*算法存在的不足。
子节点放入后继节点表中
2.2.8 对后继节点链表中的节点分类处理
利用 getbrother 函数从后继节点链表中取得一个节点,并将该节点从后继节点中移除。 对该节点依照条件,作下面的处理: 利用 atopen 函数判断该节点是否在 open 表中,如果在,则比较该节点与 open 表中状态相 同的节点到搜索树根的距离,即g(������������)的值,如果前者小于后者,则更新搜索树,并用前者替 换掉后者。
while (head_b.next!=NULL) { Node *tmp=getbrother (&head_b); //从后继节点表中取出一个节点记为 tmp,并从
6
后继节点表中删除该节点
Node * atposion; //如果在 open 表中或在 close 表中,则返回 true,并将 atposion 指 向该节点
2.2.6 判断 bestNode 所指节点是不是属于目标状态
使用 ifequal 函数判断 bestNode 是不是属于目标状态。如果属于,就成功找到目标状态, 利用 get_bestroute 函数输出最佳路径并退出。
if (bool equal=ifequal (bestNode,Sg)) { cout <<"找到目标状态!"<<endl; cout <<"请按任意键,输出最佳路径..."<<endl; system("pause");