启发式搜索 八数码问题

合集下载

启发式搜索浅谈,解决八数码问题

启发式搜索浅谈,解决八数码问题

启发式搜索浅谈,解决⼋数码问题博客迁移⾄相信很多⼈都接触过九宫格问题,也就是⼋数码问题。

问题描述如下:在3×3的棋盘,摆有⼋个棋⼦,每个棋⼦上标有1⾄8的某⼀数字,不同棋⼦上标的数字不相同。

棋盘上还有⼀个空格,与空格相邻的棋⼦可以移到空格中。

要求解决的问题是:给出⼀个初始状态和⼀个⽬标状态,找出⼀种从初始转变成⽬标状态的移动棋⼦步数最少的移动步骤。

其实在很早之前我就做过这道题了,当时我⽤的是双向⼴搜,后来⼜⼀知半解的模仿了⼀个启发式搜索(A*)。

昨天在图书馆看书的时候,翻阅了⼀下⼈⼯智能的书籍,⼜发现了这个经典的⼋数码问题。

于是便看了下去,渐渐的明⽩了启发式搜索的真正含义,才知道⾃⼰⼏年前模仿的那个代码是什么意思。

在这篇⽂章⾥,我把昨天的所学东西稍稍的记录⼀下,顺便分享给⼤家,如果有什么错误,欢迎各位指出。

平时,我们所使⽤的搜索算法⼤多数都是⼴搜(BFS)和深搜(DFS),其实他们都是盲⽬搜索,相对来说搜索的状态空间⽐较⼤,效率⽐较低。

⽽启发式搜索则相对的智能⼀些,它能对所有当前待扩展状态进⾏评估,选出⼀个最好的状态、最容易出解的状态进⾏搜索。

这样,我们就可以避免扩展⼤量的⽆效状态,从⽽提⾼搜索效率。

在对状态进⾏评估的时候,我们会使⽤到⼀个这样的等式f(n)=g(n)+h(n)。

那这个等式是什么含义呢?其中g(n)代表到达代价,即从初始状态扩展到状态n的代价值。

h(n)代表状态n的估计出解代价,即从状态n扩展到⽬标状态的代价估计值。

所以,f(n)代表估计整体代价,即⼀个搜索路径上经过状态n且成功出解的估计代价值。

于是,在启发式搜索算法中,我们只要对每⼀个状态,求出其f(n),每次取f(n)最⼩的状态进⾏扩展即可。

那现在还有⼀个问题,就是如何确定g(n)和h(n)到底是什么函数?否则f(n)也⽆法求出。

其实g(n)相对来说⽐较好确定,因为在到达状态n之后,必定有⼀条从初始状态到n的搜索路径,于是我们可以从这条路径上找出到达代价g(n),⼀般的我们就取路径长度即可。

实验三:A星算法求解8数码问题实验

实验三:A星算法求解8数码问题实验

实验三:A星算法求解8数码问题实验实验三:A*算法求解8数码问题实验一、实验目的熟悉和掌握启发式搜索的定义、估价函数和算法过程,并利用A*算法求解N数码难题,理解求解流程和搜索顺序。

二、实验内容1、八数码问题描述所谓八数码问题起源于一种游戏:在一个3×3的方阵中放入八个数码1、2、3、4、5、6、7、8,其中一个单元格是空的。

将任意摆放的数码盘(城初始状态)逐步摆成某个指定的数码盘的排列(目标状态),如图1所示图1 八数码问题的某个初始状态和目标状态对于以上问题,我们可以把数码的移动等效城空格的移动。

如图1的初始排列,数码7右移等于空格左移。

那么对于每一个排列,可能的一次数码移动最多只有4中,即空格左移、空格右移、空格上移、空格下移。

最少有两种(当空格位于方阵的4个角时)。

所以,问题就转换成如何从初始状态开始,使空格经过最小的移动次数最后排列成目标状态。

2、八数码问题的求解算法2.1 盲目搜索宽度优先搜索算法、深度优先搜索算法2.2 启发式搜索启发式搜索算法的基本思想是:定义一个评价函数f,对当前的搜索状态进行评估,找出一个最有希望的节点来扩展。

先定义下面几个函数的含义:f*(n)=g*(n)+h*(n) (1)式中g*(n)表示从初始节点s到当前节点n的最短路径的耗散值;h*(n)表示从当前节点n到目标节点g的最短路径的耗散值,f*(n)表示从初始节点s经过n到目标节点g的最短路径的耗散值。

评价函数的形式可定义如(2)式所示:f(n)=g(n)+h(n) (2)其中n是被评价的当前节点。

f(n)、g(n)和h(n)分别表示是对f*(n)、g*(n)和h*(n)3个函数值的估计值。

利用评价函数f(n)=g(n)+h(n)来排列OPEN表节点顺序的图搜索算法称为算法A。

在A算法中,如果对所有的x,h(x)<=h*(x) (3)成立,则称好h(x)为h*(x)的下界,它表示某种偏于保守的估计。

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

基于启发式搜索算法A星解决八数码问题
//定义算法中用到的链表,图,树的节点的结构。 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 函数的值 };
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 的父节点的
while (head_b.next!=NULL) { Node *tmp=getbrother (&head_b); //从后继节点表中取出一个节点记为 tmp,并从

八个数字问题实验报告.doc

八个数字问题实验报告.doc

八个数字问题实验报告. 《八数码问题》实验报告首先,实验的目的:熟悉启发式搜索算法。

二、实验内容:启发式搜索算法用于解决8位数问题。

编制了程序,实现了解决8位数问题的算法。

采用评估功能,其中:是搜索树中节点的深度;在节点数据库中放错位置的件数;这是每个棋子与其在节点数据库中的目标位置之间距离的总和。

三、实验原理:1.问题描述:八位数问题也被称为九宫问题。

在3×3的棋盘上,有八个棋子,每一个棋子都标有一定的1到8的数字,不同棋子上标的数字是不同的。

棋盘上还有一个空格(用数字0表示),与空格相邻的棋子可以移动到空格中。

要解决的问题是: 给定初始状态和目标状态,找出从初始状态到目标状态移动次数最少的移动步骤。

所谓问题的一种状态是棋盘上棋子的排列。

解决八位数问题实际上是找出一系列从初始状态到目标状态的中间过渡状态。

2.原则描述:启发式搜索(1)原理启发式搜索是评估每个搜索在状态空间中的位置以获得最佳位置,然后从这个位置搜索到目标。

这样,可以省略大量不必要的搜索路径,并且提高了效率。

在启发式搜索中,位置的评估非常重要。

不同的评估会产生不同的效果。

(2)评估函数计算节点的评估函数,可分为两部分:1.成本已经支付(从开始节点到当前节点);2.要支付的价格(当前节点到目标节点)。

节点n的评估函数被定义为从初始节点通过n到目标节点的路径的最小成本的估计值,即=。

是从初始节点到达当前节点n的实际成本;是从节点n到目标节点的最佳路径的估计开销。

比例越大,它越倾向于先搜索宽度或同等成本。

相反,比例越大,启发式性能越强。

(3)算法描述:(1)将起始节点S放入OPEN表中,计算节点S的值;(2)如果OPEN为空表,则无法退出且没有解决方案;(3)从OPEN表中选择具有最小值的节点。

如果多个节点具有相同的值,当其中一个节点是目标节点时,选择目标节点;否则,任意一个节点被选为节点;(4)从OPEN表中移除节点,并将其放入CLOSED扩展节点表中;(5)如果它是目标节点,它成功退出并获得解决方案;⑥扩展节点以生成其所有后续节点。

人工智能实验一_八数码问题

人工智能实验一_八数码问题

用A*算法解决八数码问题1 问题描述1.1 待解决问题的解释八数码游戏(八数码问题)描述为:在3×3组成的九宫格棋盘上,摆有八个将牌,每一个将牌都刻有1-8八个数码中的某一个数码。

棋盘中留有一个空格,允许其周围的某一个将牌向空格移动,这样通过移动将牌就可以不断改变将牌的布局。

这种游戏求解的问题是:给定一种初始的将牌布局或结构(称初始状态)和一个目标的布局(称目标状态),问如何移动将牌,实现从初始状态到目标状态的转变。

1.2 问题的搜索形式描述(4要素)初始状态:8个数字将牌和空格在九宫格棋盘上的所有格局组成了问题的状态空间。

其中,状态空间中的任一种状态都可以作为初始状态。

后继函数:通过移动空格(上、下、左、右)和周围的任一棋子一次,到达新的合法状态。

目标测试:比较当前状态和目标状态的格局是否一致。

路径消耗:每一步的耗散值为1,因此整个路径的耗散值是从起始状态到目标状态的棋子移动的总步数。

1.3 解决方案介绍(原理)对于八数码问题的解决,首先要考虑是否有答案。

每一个状态可认为是一个1×9的矩阵,问题即通过矩阵的变换,是否可以变换为目标状态对应的矩阵?由数学知识可知,可计算这两个有序数列的逆序值,如果两者都是偶数或奇数,则可通过变换到达,否则,这两个状态不可达。

这样,就可以在具体解决问题之前判断出问题是否可解,从而可以避免不必要的搜索。

如果初始状态可以到达目标状态,那么采取什么样的方法呢?常用的状态空间搜索有深度优先和广度优先。

广度优先是从初始状态一层一层向下找,直到找到目标为止。

深度优先是按照一定的顺序前查找完一个分支,再查找另一个分支,以至找到目标为止。

广度和深度优先搜索有一个很大的缺陷就是他们都是在一个给定的状态空间中穷举。

这在状态空间不大的情况下是很合适的算法,可是当状态空间十分大,且不预测的情况下就不可取了。

他的效率实在太低,甚至不可完成。

由于八数码问题状态空间共有9!个状态,对于八数码问题如果选定了初始状态和目标状态,有9!/2个状态要搜索,考虑到时间和空间的限制,在这里采用A*算法作为搜索策略。

用A算法解八数码问题

用A算法解八数码问题

用A*算法解八数码问题1.启发式搜索广度优先搜索和双向广度优先搜索都属于盲目搜索,这在状态空间不大的情况下是很合适的算法,可是当状态空间十分庞大时,它们的效率实在太低,往往都是在搜索了大量无关的状态结点后才碰到解答,甚至更本不能碰到解答。

搜索是一种试探性的查寻过程,为了减少搜索的盲目性引,增加试探的准确性,就要采用启发式搜索了。

所谓启发式搜索就是在搜索中要对每一个搜索的位置进行评估,从中选择最好、可能容易到达目标的位置,再从这个位置向前进行搜索,这样就可以在搜索中省略大量无关的结点,提高了效率。

2.A*算法A*算法是一种常用的启发式搜索算法。

在A*算法中,一个结点位置的好坏用估价函数来对它进行评估。

A*算法的估价函数可表示为:f'(n) = g'(n) + h'(n)这里,f'(n)是估价函数,g'(n)是起点到终点的最短路径值(也称为最小耗费或最小代价),h'(n)是n到目标的最短路经的启发值。

由于这个f'(n)其实是无法预先知道的,所以实际上使用的是下面的估价函数:f(n) = g(n) + h(n)其中g(n)是从初始结点到节点n的实际代价,h(n)是从结点n到目标结点的最佳路径的估计代价。

在这里主要是h(n)体现了搜索的启发信息,因为g(n)是已知的。

用f(n)作为f'(n)(1)g(n)>=g'(n)的近似,也就是用g(n)代替g'(n),h(n)代替h'(n)。

这样必须满足两个条件:(大多数情况下都是满足的,可以不用考虑),且f必须保持单调递增。

(2)h必须小于等于实际的从当前节点到达目标节点的最小耗费h(n)<=h'(n)。

第二点特别的重要。

可以证明应用这样的估价函数是可以找到最短路径的。

3.A*算法的步骤A*算法基本上与广度优先算法相同,但是在扩展出一个结点后,要计算它的估价函数,并根据估价函数对待扩展的结点排序,从而保证每次扩展的结点都是估价函数最小的结点。

八数码问题的启发式计算

八数码问题的启发式计算

八数码问题的启发式计算
八数码问题是一个十分经典的启发式搜索问题,其中启发式计算可以用来评估搜索状态的优先级。

启发式计算是通过一定的估计方法来估测当前状态距离目标状态的距离或成本,从而帮助搜索算法选择最有希望的路径。

在八数码问题中,可以使用曼哈顿距离作为启发式函数。

曼哈顿距离可以通过将当前状态和目标状态分别转化为二维坐标,并计算两个坐标之间的曼哈顿距离来表示状态之间的距离。

具体步骤如下:
1. 将当前状态和目标状态转换为二维坐标:将每个数字在三乘三的游戏板中的位置映射为坐标,例如将数字1映射为(0, 0),数字2映射为(1, 0),以此类推。

2. 对于当前状态的每个数字,计算它与目标状态对应数字的曼哈顿距离:通过求两个数字的坐标的横向距离和纵向距离之和来计算曼哈顿距离。

例如,对于数字4在当前状态的坐标是(1,
2),在目标状态的坐标是(0, 2),则它们之间的曼哈顿距离为1。

3. 将所有数字的曼哈顿距离相加,得到当前状态的启发式函数值:将步骤2中计算的所有数字的曼哈顿距离相加,得到当前状态的启发式函数值。

4. 让搜索算法根据启发式函数值优先选择具有较小启发式函数值的状态进行搜索。

使用曼哈顿距离作为启发式计算的方法可以在一定程度上优化搜索算法的效率,使得更有希望到达目标状态的路径更有可能被优先选择。

但需要注意的是,曼哈顿距离只是一种启发式函
数,不能保证一定能找到最优解,可能存在剪枝路径的情况。

若需要保证找到最优解,可以使用A*算法结合曼哈顿距离进行搜索。

启发式搜索算法

启发式搜索算法

人工智能基础实验报告实验名称:八数码问题姓名:张俊学号:2220092333指导老师:邓安生启发式搜索算法1. 实验内容:使用启发式搜索算法求解8数码问题。

⑴ 编制程序实现求解8数码问题A *算法,采用估价函数()()()()w n f n d n p n ⎧⎪=+⎨⎪⎩, 其中:()d n 是搜索树中结点n 的深度;()w n 为结点n 的数据库中错放的棋子个数;()p n 为结点n 的数据库中每个棋子与其目标位置之间的距离总和。

⑵ 分析上述⑴中两种估价函数求解8数码问题的效率差别,给出一个是()p n 的上界的()h n 的定义,并测试使用该估价函数是否使算法失去可采纳性。

2. 实验目的熟练掌握启发式搜索A *算法及其可采纳性。

3. 实验原理八数码问题是在3行和3列构成的九宫棋盘上放置数码为1到8的8个棋盘,剩下一个空格的移动来不断改变棋盘的布局,求解这类问题的方法是:给定初始布局(即初始状态)和目标布局(即目标状态),定义操作算子的直观方法是为每个棋牌制定一套可能的走步》上,下,左,右四种移动,再根据所定义的启发式搜索函数在搜索过程中选择最合适的操作算子,得到最优的路径。

4.源代码#include <iomanip> #include <stdlib.h> #include <time.h> #include <iostream> #include <stdio.h> #include <conio.h>#include <math.h>//以上为C++源文件 using namespace std; static int space=0; int target[9];class EightNum//定义一个EightNum 类 {public:int num[9];int f;//初始状态与目标状态相比,棋子错放个数int deap;//深度int evalfun;//状态的估价值EightNum *parent;//以下为类内成员函数的声明EightNum(int nnum[9]);int get_evalfun();int get_deapfun();void eval_func(int id);int Canspread(int n);void Spreadchild(int n);void getnum(int num1[9]);void setnum(int num1[9]);void show(void);int operator ==(EightNum& NewEightN);int operator ==(int num2[9]);int Shownum();};//-----------------------以下为EightNum类成员函数定义-----------------// class Stack{private:EightNum * eightnum;public:Stack * next;EightNum * Minf();EightNum * Belong(EightNum * suc);void Putinto(EightNum * suc);};EightNum::EightNum(int nnum[9]){//此函数功能为:初始化num[];for(int i=0;i<9;i++)num[i]=nnum[i];f=0;deap=0;parent=NULL;}int EightNum::get_evalfun(){return evalfun;}int EightNum::get_deapfun(){return deap;}void EightNum::eval_func(int id){//此函数为估价函数int i,qifa;qifa=0;switch(id){case 1:{for(i=0;i<9;i++){if(num[i]!=target[i])qifa++;}break;}case 2:{int j, h1,h2;for(i=0;i<9;i++){for(j=0;j<9;j++){if(num[j]==i)h1=j;if(target[j]==i)h2=j;}qifa+=(int)(fabs((double)(h1/3 - h2/3)) + fabs((double)(h1%3 - h2%3)));}break;}case 3:{int j, h1,h2;for(i=0;i<9;i++){for(j=0;j<9;j++){if(num[j]==i)h1=j;if(target[j]==i)h2=j;}qifa+=(int)(fabs((double)(h1/3 - h2/3)) + fabs((double)(h1%3 - h2%3)));}qifa=3*qifa;break;}default :break;}f=qifa;if(this->parent==NULL) deap=0;else deap=this->parent->deap+1;evalfun=deap+f;}int EightNum::Canspread(int n){//判断空格"0"可否移动int i,flag = 0;for(i = 0;i < 9;i++)if(this->num[i] == 0)break;switch(n){case 1:if(i/3 != 0)flag = 1;break;case 2:if(i/3 != 2)flag = 1;break;case 3:if(i%3 != 0)flag = 1;break;case 4:if(i%3 != 2)flag = 1;break;default:break;}return flag ;}void EightNum::Spreadchild(int n){//扩展child节点的子节点int i,loc,qifa;for(i = 0;i < 9;i++)this->num[i] = this->parent->num[i];for(i = 0;i < 9;i++)if(this->num[i] == 0)break;if(n==0)loc = i%3+(i/3 - 1)*3;else if(n==1)loc = i%3+(i/3 + 1)*3;else if(n==2)loc = i%3-1+(i/3)*3;elseloc = i%3+1+(i/3)*3;qifa = this->num[loc];this->num[i] = qifa;this->num[loc] = 0;}void EightNum::getnum(int num1[9]){ for(int i=0;i<9;i++)num1[i]=num[i];}void EightNum::setnum(int num1[9]){ for(int i=0;i<9;i++)num[i]=num1[i];}void EightNum::show(){//输出函数for(int i=0;i<9;i++){cout<<num[i]<<" ";if((i+1)%3==0)cout<<"\n";}cout<<"--------------------";}int EightNum::Shownum(){if(this == NULL)return 0;else{int n = this->parent->Shownum();this->show();cout<<endl;return n+1;}}int EightNum::operator ==(EightNum& NewEightN){int compere=1;for(int i=0;i<9;i++)if(num[i]!=NewEightN.num[i]){compere=0;break;}if(compere==0) return 0;else return 1;}//-----------------------以下为分函数的定义---------------------////判断是否有解的函数int solve(int num[9],int target[9]){int i,j;int num_con=0,tar_con=0;for(i=0;i<9;i++)for(j=0;j<i;j++){if(num[j]<num[i] && num[j]!=0)num_con++;if(target[j]<target[i] && target[j]!=0)tar_con++;}num_con=num_con%2;tar_con=tar_con%2;if((num_con==0 && tar_con==0)||(num_con==1 && tar_con==1))return 1;elsereturn 0;}EightNum * Stack::Minf(){Stack * qifa =this->next;Stack * min = this->next;Stack * minp = this;EightNum * minx;while(qifa->next != NULL){if((qifa->next->eightnum->get_evalfun()) < (min->eightnum->get_evalfun())){min = qifa->next;minp = qifa;}qifa = qifa->next;}minx = min->eightnum;qifa = minp->next;minp->next = minp->next->next;free(qifa);return minx;}//判断节点是否属于OPEN表或CLOSED表EightNum * Stack::Belong(EightNum * suc){Stack * qifa = this-> next ;if(qifa == NULL)return NULL;while(qifa != NULL){if(suc==qifa->eightnum)return qifa ->eightnum;qifa = qifa->next;}return NULL;}//把节点存入OPEN 或CLOSED 表中void Stack::Putinto(EightNum * suc){Stack * qifa;qifa =(Stack *) malloc(sizeof(Stack));qifa->eightnum = suc;qifa->next = this->next;this->next = qifa;}int BelongProgram(EightNum * suc ,Stack *Open ,Stack *Closed ,EightNum goal,int m ){EightNum * qifa = NULL;int flag = 0;if((Open->Belong(suc) != NULL) || (Closed->Belong(suc) != NULL)){if(Open->Belong(suc) != NULL) qifa = Open->Belong(suc);else qifa = Closed->Belong(suc);flag=1;}else{Open->Putinto(suc);suc->eval_func(m);}return flag;}//扩展后继节点总函数void Spread(EightNum * suc, Stack * Open, Stack * Closed, EightNum goal,int m){int i;EightNum * child;for(i = 0; i < 4; i++){if(suc->Canspread(i+1)){space++;child = (EightNum *) malloc(sizeof(EightNum));child->parent = suc;child->Spreadchild(i);child->eval_func(m);if(BelongProgram(child, Open, Closed, goal,m)) //判断子节点是否属于OPEN或CLOSED表free(child);}}}//执行函数EightNum * Process(EightNum * org, EightNum goal, Stack * Open, Stack * Closed,int m){while(1){if(Open->next == NULL)return NULL;EightNum * minf =Open->Minf();Closed->Putinto(minf);if((*minf)==goal)return minf;Spread(minf, Open, Closed, goal,m);}}//------------------------A*算法搜索函数----------------------//void A(int id,EightNum start,EightNum Target){EightNum * result;space=0;float time;Stack *Open = (Stack *) malloc(sizeof(Stack));Open->next = NULL;Stack *Closed = (Stack *) malloc(sizeof(Stack));Closed->next = NULL;clock_t startt,finisht;startt=clock();//开始时间start.eval_func(id);Open->Putinto(&start);result = Process(&start, Target, Open, Closed,id); //进行剩余的操作cout<<"\n搜索过程:\n"<<result->Shownum()<<endl;finisht=clock();time=(float)(finisht-startt);cout<<endl<<id<<"算法处理结果:所耗时间:";cout<<time;cout<<"ms, ";cout<<"所耗空间:";cout<<space;cout<<"块, "<<endl<<endl;}//-----------------------------主函数-----------------------------//int main(void)//主函数{int i,j;int flag;int num[9];int error;do{error=0;cout<<"请输入八数码问题的初始状态(0代表空格,“棋子”间用空格隔开):"<<endl;for(i=0;i<9;i++){flag=0;cin>>num[i];for(j=0;j<i;j++)if(num[j]==num[i])flag=1;if(num[i]<0||num[i]>8||flag==1){error++;}}if(error!=0)cout<<"输入数据错误!请重新输入!"<<endl;}while(error!=0);//输入八数码问题的初始状态(0代表空格,“棋子”间用空格隔开);int error1;do{error1=0;cout<<"请输入新的目标状态(用0代表空格,“棋子”间用空格隔开):"<<endl;for(i=0;i<9;i++){flag=0;cin>>target[i];for(j=0;j<i;j++)if(target[j]==target[i])flag=1;if(target[i]<0||target[i]>9||flag==1){error1++;}}if(error1!=0)cout<<"输入数据错误!请重新输入!"<<endl;}while(error1!=0);//输入八数码问题的目标状态(用0代表空格,中间用空格隔开);EightNum start(num),Target(target);int m=solve(num,target);//判断初始状态到目标状态是否有解,有解返回1,误解返回0;if(m==0){cout<<"此状态无解!"<<endl;return 0;}int id=0;while(id!=3){cout<<"1. 错放的棋子个数为;\n2.每个棋子与目标位置之间的距离总和为;"<<endl;cout<<"3.结束,退出程序!"<<endl;cout<<"\n请选择功能,分别输入“1”“2”“3”进行选择:"<<endl;cin>>id;switch(id){case 1:{cout<<"错放的棋子个数结果为:\n(以下逐一展示搜索过程:)"<<endl;A(1,start,Target);break;}case 2:{cout<<"每个棋子与其目标位置之间的距离总和为:\n(以下逐一展示搜索过程:)"<<endl;A(2,start,Target);break;}default: break;}}cout<<"啊啊….程序结束!!";}实验截图实验中遇到的问题1:开始程序只能运行一种方式即按照错位个数搜索,后经过查找相关资料,修改后可程序可进行选择,两种方法结合在一起根据选择运行。

启发式搜索-八数码问题

启发式搜索-八数码问题

启发式搜索1. 介绍八数码问题也称为九宫问题。

在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。

棋盘上还有一个空格〔以数字0来表示〕,与空格相邻的棋子可以移到空格中。

要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。

所谓问题的一个状态就是棋子在棋盘上的一种摆法。

解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态。

2. 使用启发式搜索算法求解8数码问题。

1) A ,A 星算法采用估价函数()()()()w n f n d n p n ⎧⎪=+⎨⎪⎩, 其中:()d n 是搜索树中结点n 的深度;()w n 为结点n 的数据库中错放的棋子个数;()p n 为结点n 的数据库中每个棋子与其目标位置之间的距离总和。

2)宽度搜索采用f(i)为i 的深度,深度搜索采用f(i)为i 的深度的倒数。

3. 算法流程① 把起始节点S 放到OPEN 表中,并计算节点S 的)(S f ;② 如果OPEN 是空表,则失败退出,无解;③ 从OPEN 表中选择一个f 值最小的节点i 。

如果有几个节点值相同,当其中有一个 为目标节点时,则选择此目标节点;否则就选择其中任一个节点作为节点i ; ④ 把节点i 从 OPEN 表中移出,并把它放入 CLOSED 的已扩展节点表中; ⑤ 如果i 是个目标节点,则成功退出,求得一个解;⑥ 扩展节点i ,生成其全部后继节点。

对于i 的每一个后继节点j :计算)(j f ;如果j 既不在OPEN 表中,又不在CLOCED 表中,则用估价函数f 把 它添入OPEN 表中。

从j 加一指向其父节点i 的指针,以便一旦找到目标节点时记住一个解答路径;如果j 已在OPEN 表或CLOSED 表中,则比较刚刚对j 计算过的f 和前面计算过的该节点在表中的f 值。

如果新的f 较小,则(I)以此新值取代旧值。

课程设计 用A算法解决8数码问题

课程设计 用A算法解决8数码问题

课程设计用A算法解决8数码问题
8数码问题是一个组合优化问题,是指给定8个数字,请将这些数字填入九宫格,使
九宫格中每行、列、粗实线和细实线中的数字之和都相等。

本文重点讨论的是用A算法解
决8数码问题的方法,即A算法估价函数。

A算法属于启发式搜索,它的原理是:先计算当前状态的分数,再根据该分数估计状
态所代表的最终的价值,以作为当前局面的启发,判断当前局面是否是最优局面。

针对 8数码问题,A算法估价函数可计算九宫格每行、列和粗实线差值总和,它代表
九宫格中九位之和是如何与其目标值(45)的偏差程度。

根据九宫格中九位之和的偏差程
度定义该九宫格的分数:若九位之和与其目标值相等,则分数成为 0;若差值跨越两个数字,则分数变为 2;若差值跨越一个数字,则分数变为 1。

有了这一定义,A算法便可应
用在8数码问题中了。

正如上述,A算法估价函数的总分数可由九宫格所有表项的偏差程度来定义,若九宫
格所有表项的结果均跟其目标值(45)相等,总分数则为 0,反之则不是,总分数就会根
据表项有多大的偏差程度来决定。

然后A算法搜索遍历到的每个状态都可以根据它对应的
分数计算当前状态的价值,以作为启发,最终定位最优状态。

从理论上讲,A算法可以在求解8数码问题时取得良好的运算的结果,它可以很好的
评估问题的最优解,因此使得搜索树更加有效,从而减少计算机运算时间,提升解答效率。

人工智能-A算法求解8数码问题

人工智能-A算法求解8数码问题

实验四 A*算法求解8数码问题一、实验目的熟悉和掌握启发式搜索的定义、估价函数和算法过程,并利用A*算法求解8数码难题,理解求解流程和搜索顺序。

二、实验原理A*算法是一种启发式图搜索算法,其特点在于对估价函数的定义上。

对于一般的启发式图搜索,总是选择估价函数f值最小的节点作为扩展节点。

因此,f 是根据需要找到一条最小代价路径的观点来估算节点的,所以,可考虑每个节点n的估价函数值为两个分量:从起始节点到节点n的实际代价g(n)以及从节点n 到达目标节点的估价代价h(n),且h(n)<=h*(n),h*(n)为n节点到目标节点的最优路径的代价。

八数码问题是在3×3的九宫格棋盘上,排放有8个刻有1~8数码的将牌。

棋盘中有一个空格,允许紧邻空格的某一将牌可以移到空格中,这样通过平移将牌可以将某一将牌布局变换为另一布局。

针对给定的一种初始布局或结构(目标状态),问如何移动将牌,实现从初始状态到目标状态的转变。

如图1所示表示了一个具体的八数码问题求解。

图1 八数码问题的求解三、实验内容1、参考A*算法核心代码,以8数码问题为例实现A*算法的求解程序(编程语言不限),要求设计两种不同的估价函数。

2、在求解8数码问题的A*算法程序中,设置相同的初始状态和目标状态,针对不同的估价函数,求得问题的解,并比较它们对搜索算法性能的影响,包括扩展节点数、生成节点数等。

3、对于8数码问题,设置与图1所示相同的初始状态和目标状态,用宽度优先搜索算法(即令估计代价h(n)=0的A*算法)求得问题的解,记录搜索过程中的扩展节点数、生成节点数。

4、提交实验报告和源程序。

四.实验截图五.源代码#include<iostream>#include"stdio.h"#include"stdlib.h"#include"time.h"#include"string.h"#include<queue>#include<stack>using namespace std;const int N=3;//3*3棋?盘ìconst int Max_Step=32;//最?大洙?搜?索÷深?度èenum Direction{None,Up,Down,Left,Right};//方?向ò,?分?别纄对?应畖上?下?左哩?右?struct Chess//棋?盘ì{int chessNum[N][N];//棋?盘ì数簓码?int Value;//评à估à值μDirection BelockDirec;//所ù屏á蔽?方?向òstruct Chess * Parent;//父?节ú点?};void PrintChess(struct Chess *TheChess);//打洙?印?棋?盘ìstruct Chess * MoveChess(struct Chess * TheChess,Direction Direct,bool CreateNewChess);//移?动ˉ棋?盘ì数簓字?int Appraisal(struct Chess * TheChess,struct Chess * Target);//估à价?函ˉ数簓struct Chess * Search(struct Chess* Begin,struct Chess * Target);//A*搜?索÷函ˉ数簓int main(){//本?程ì序ò的?一?组哩?测a试?数簓据Y为a/*初?始?棋?盘ì*1 4 0**3 5 2**6 7 8**//*目?标括?棋?盘ì*0 1 2**3 4 5**6 7 8**/Chess Target;Chess *Begin,*ChessList;Begin=new Chess;int i;cout<<"请?输?入?初?始?棋?盘ì,?各÷数簓字?用?空?格?隔?开a:阰"<<endl;for(i=0;i<N;i++){for(int j=0;j<N;j++){cin>>Begin->chessNum[i][j];}}cout<<"请?输?入?目?标括?棋?盘ì,?各÷数簓字?用?空?格?隔?开a:阰"<<endl;for(i=0;i<N;i++){for(int j=0;j<N;j++){cin>>Target.chessNum[i][j];}}//获?取?初?始?棋?盘ìAppraisal(Begin,&Target);Begin->Parent=NULL;Begin->BelockDirec=None;Target.Value=0;cout<<"初?始?棋?盘ì:";PrintChess(Begin);cout<<"目?标括?棋?盘ì:";PrintChess(&Target);ChessList=Search(Begin,&Target);//搜?索÷//打洙?印?if(ChessList){/*将?返う?回?的?棋?盘ì列表括?利?用?栈?将?其?倒?叙e*/Chess *p=ChessList;stack<Chess *>Stack;while(p->Parent!=NULL){Stack.push(p);p=p->Parent;}cout<<"搜?索÷结á果?:"<<endl;int num=1;while(!Stack.empty()){cout<<"第台?<<num<<"步?: ";num++;PrintChess(Stack.top());Stack.pop();}cout<<"\n完?成é!"<<endl;}elsecout<<"搜?索÷不?到?结á果?,?搜?索÷深?度è大洙?于?2\n"<<endl;return 0;}//打洙?印?棋?盘ìvoid PrintChess(struct Chess *TheChess){cout<<"(评à估à值μ为a";cout<<TheChess->Value;cout<<")"<<endl;for(int i=0;i<N;i++){cout<<" ";for(int j=0;j<N;j++){cout<<TheChess->chessNum[i][j]<<" ";}cout<<endl;}}//移?动ˉ棋?盘ìstruct Chess * MoveChess(struct Chess * TheChess,Direction Direct,bool CreateNewChess) {struct Chess * NewChess;//获?取?空?闲D格?位?置?int i,j;for(i=0;i<N;i++){bool HasGetBlankCell=false;for(j=0;j<N;j++){if(TheChess->chessNum[i][j]==0){HasGetBlankCell=true;break;}}if(HasGetBlankCell)break;}int ii=i,jj=j;bool AbleMove=true;//判D断?是?否?可é以?移?动ˉswitch(Direct){case Up:i++;if(i>=N)AbleMove=false;break;case Down:i--;if(i<0)AbleMove=false;break;case Left:j++;if(j>=N)AbleMove=false;break;case Right:j--;if(j<0)AbleMove=false;break;};if(!AbleMove)//不?可é以?移?动ˉ则ò返う?回?原-节ú点?{return TheChess;}if(CreateNewChess){NewChess=new Chess();for(int x=0;x<N;x++){for(int y=0;y<N;y++)NewChess->chessNum[x][y]=TheChess->chessNum[x][y];//创洹?建¨新?棋?盘ì,?此?时骸?值μ与?原-棋?盘ì一?致?}}elseNewChess=TheChess;NewChess->chessNum[ii][jj] = NewChess->chessNum[i][j];//移?动ˉ数簓字?NewChess->chessNum[i][j]=0;//将?原-数簓字?位?置?设Θ?置?为a空?格?return NewChess;}//估à价?函ˉ数簓int Appraisal(struct Chess * TheChess,struct Chess * Target){int Value=0;for(int i=0;i<N;i++){for(int j=0;j<N;j++){if(TheChess->chessNum[i][j]!=Target->chessNum[i][j])Value++;}}TheChess->Value=Value;return Value;}//A*搜?索÷函ˉ数簓struct Chess * Search(struct Chess* Begin,struct Chess * Target){Chess *p1,*p2,*p;int Step=0;//深?度èp=NULL;queue<struct Chess *> Queue;Queue.push(Begin);//初?始?棋?盘ì入?队ó//搜?索÷do{p1=(struct Chess *)Queue.front();Queue.pop();//出?队ófor(int i=1;i<=4;i++)//分?别纄从洙?四?个?方?向ò推?导?出?新?子哩?节ú点? {Direction Direct=(Direction)i;if(Direct==p1->BelockDirec)//跳?过y屏á蔽?方?向òcontinue;p2=MoveChess(p1,Direct,true);//移?动ˉ数簓码?if(p2!=p1)//数簓码?是?否?可é以?移?动ˉ{Appraisal(p2,Target);//对?新?节ú点?估à价?if(p2->Value<=p1->Value)//是?否?为a优?越?节ú点?{p2->Parent=p1;switch(Direct)//设Θ?置?屏á蔽?方?向ò,防え?止1往?回?推?{case Up:p2->BelockDirec=Down;break;case Down:p2->BelockDirec=Up;break;case Left:p2->BelockDirec=Right;break;case Right:p2->BelockDirec=Left;break;}Queue.push(p2);//存?储洹?节ú点?到?待鋣处鋦理え?队ó列if(p2->Value==0)//为a0则ò,搜?索÷完?成é{p=p2;i=5;}}else{delete p2;//为a劣ⅷ?质ê节ú点?则ò抛×弃úp2=NULL;}}}Step++;if(Step>Max_Step)return NULL;}while(p==NULL || Queue.size()<=0);return p;}六、实验报告要求1、分析不同的估价函数对A*搜索算法性能的影响等。

启发式搜索解决八数码问题

启发式搜索解决八数码问题

#include"stdio.h"int goal[9]={1,2,3,8,0,4,7,6,5},sgoal[9];//goal为棋盘的目标布局,并用中间状态sgoal与之比较struct Board{int pos[9];int d,f,e;//d:深度;f:启发函数;e:记录前一次的扩展节点};struct NodeLink{Board boardstate;NodeLink *parent;NodeLink *previous;NodeLink *next;NodeLink *path;};//更新纪录八数码的状态void setboard(int a[],int b[],int flag) //flag=0,写棋子;flag=1,写棋盘{for(int i=0;i<=8;i++)if(flag)a[b[i]]=i;elseb[a[i]]=i;}//计算启发值的函数int calvalue(int a[]) //不在位棋子数{int c=0;for(int i=0;i<=8;i++)if(a[i]!=goal[i])if(goal[i]!=0)c++;return c;}//生成一个新节点的函数NodeLink *makenode(NodeLink *TEM,int depth,int flag){NodeLink *temp=new NodeLink;for(int i=0;i<=8;i++)temp->boardstate.pos[i]=TEM->boardstate.pos[i];switch(flag){case 1:{temp->boardstate.pos[0]--;temp->boardstate.pos[sgoal[temp->boardstate.pos[0]]]++; //向左移break;}case 2:{temp->boardstate.pos[0]++;temp->boardstate.pos[sgoal[temp->boardstate.pos[0]]]--; //向右移break;}case 3:{temp->boardstate.pos[0]-=3;temp->boardstate.pos[sgoal[temp->boardstate.pos[0]]]+=3; //向上移break;}case 4:{temp->boardstate.pos[0]+=3;temp->boardstate.pos[sgoal[temp->boardstate.pos[0]]]-=3; //向下移break;}}temp->boardstate.d=depth+1;setboard(sgoal,temp->boardstate.pos,1);temp->boardstate.f=temp->boardstate.d+calvalue(sgoal);temp->boardstate.e=flag;temp->parent=TEM;return temp;}//把新节点加入OPEN队列NodeLink *addnode(NodeLink *head,NodeLink *node) //把node插入到head链中{NodeLink *TEM;TEM=head;head=node;head->next=TEM;head->previous=NULL;if(TEM)TEM->previous=head; //TEM已为空,无需操作return head;}//求启发值最小的结点NodeLink *minf(NodeLink *head){NodeLink *min,*forward;min=head;forward=head;while(forward){if(min->boardstate.f>forward->boardstate.f)min=forward;forward=forward->next;}return min;}int main( ){int depth=0;int source[9];int i,j;NodeLink *OPEN=new NodeLink;NodeLink *TEMP,*TEM;printf("请输入初始状态:\n");for(i=0;i<9;i++)scanf("%d",&source[i]);setboard(source,OPEN->boardstate.pos,0);OPEN->boardstate.d=depth;OPEN->boardstate.e=0;OPEN->boardstate.f=depth+calvalue(source);OPEN->next=NULL;OPEN->previous=NULL;OPEN->parent=NULL;while(OPEN){TEMP=minf(OPEN); //求具有最小启发值的节点setboard(sgoal,TEMP->boardstate.pos,1); //写棋盘if(!calvalue(sgoal))break;if(TEMP!=OPEN) //如果不是第一个节点{TEMP->previous->next=TEMP->next;TEMP->next->previous=TEMP->previous;}else//是第一个节点{if(OPEN->next) //如果还有节点{OPEN=OPEN->next;OPEN->previous=NULL;}else OPEN=NULL; //否则置为空}if(TEMP->boardstate.pos[0]-1>=0&&TEMP->boardstate.e!=2) //防止棋子回到原状态 OPEN=addnode(OPEN,makenode(TEMP,depth,1));if(TEMP->boardstate.pos[0]+1<=8&&TEMP->boardstate.e!=1)OPEN=addnode(OPEN,makenode(TEMP,depth,2));if(TEMP->boardstate.pos[0]-3>=0&&TEMP->boardstate.e!=4)OPEN=addnode(OPEN,makenode(TEMP,depth,3));if(TEMP->boardstate.pos[0]+3<=8&&TEMP->boardstate.e!=3)OPEN=addnode(OPEN,makenode(TEMP,depth,4));depth++;}if(OPEN) //如有解,则打印出解的步骤{printf("共需%d 步即可完成。

启发式搜索解决八数码问题

启发式搜索解决八数码问题

#include<stdlib.h>#include<stdio.h>#include<math.h>typedef struct Node{//节点结构体int data[9];double f,g;struct Node * parent;}Node,*Lnode;typedef struct Stack{//OPEN CLOSED 表结构体Node * npoint;struct Stack * next;}Stack,* Lstack;Node * Minf(Lstack * Open){//选取OPEN表上f值最小的节点,返回该节点地址Lstack temp = (*Open)->next,min = (*Open)->next,minp = (*Open);Node * minx;while(temp->next != NULL){if((temp->next ->npoint->f) < (min->npoint->f)){min = temp->next;minp = temp;}temp = temp->next;}minx = min->npoint;temp = minp->next;minp->next = minp->next->next;free(temp);return minx;}int Canslove(Node * suc, Node * goal){//判断是否可解int a = 0,b = 0,i,j;for(i = 1; i< 9;i++)for(j = 0;j < i;j++){if((suc->data[i] > suc->data[j]) && suc->data[j] != 0)a++;if((goal->data[i] > goal->data[j]) && goal->data[j] != 0)b++;}if(a%2 == b%2)return 1;else return 0;}int Equal(Node * suc,Node * goal){//判断节点是否相等,相等,不相等for(int i = 0; i < 9; i ++ )if(suc->data[i] != goal->data[i])return 0;return 1;}Node * Belong(Node * suc,Lstack * list){//判断节点是否属于OPEN表或CLOSED表,是则返回节点地址,否则返回空地址Lstack temp = (*list) -> next ;if(temp == NULL)return NULL;while(temp != NULL){if(Equal(suc,temp->npoint))return temp -> npoint;temp = temp->next;}return NULL;}void Putinto(Node * suc,Lstack * list){//把节点放入OPEN 或CLOSED 表中Stack * temp;temp =(Stack *) malloc(sizeof(Stack));temp->npoint = suc;temp->next = (*list)->next;(*list)->next = temp;}///////////////计算f值部分-开始//////////////////////////////double Fvalue(Node suc, Node goal, float speed){//计算f值double Distance(Node,Node,int);double h = 0;for(int i = 1; i <= 8; i++)h = h + Distance(suc, goal, i);return h*speed + suc.g; //f = h + g;(speed值增加时搜索过程以找到目标为优先因此可能不会返回最优解)}double Distance(Node suc, Node goal, int i){//计算方格的错位距离int k,h1,h2;for(k = 0; k < 9; k++){if(suc.data[k] == i)h1 = k;if(goal.data[k] == i)h2 = k;}return double(fabs(h1/3 - h2/3) + fabs(h1%3 - h2%3));}///////////////计算f值部分-结束/////////////////////////////////////////////////////扩展后继节点部分的函数-开始/////////////////int BelongProgram(Lnode * suc ,Lstack * Open ,Lstack * Closed ,Node goal ,float speed) {//判断子节点是否属于OPEN或CLOSED表并作出相应的处理Node * temp = NULL;int flag = 0;if((Belong(*suc,Open) != NULL) || (Belong(*suc,Closed) != NULL)){if(Belong(*suc,Open) != NULL) temp = Belong(*suc,Open);else temp = Belong(*suc,Closed);if(((*suc)->g) < (temp->g)){temp->parent = (*suc)->parent;temp->g = (*suc)->g;temp->f = (*suc)->f;flag = 1;}}else{Putinto(* suc, Open);(*suc)->f = Fvalue(**suc, goal, speed);}return flag;}int Canspread(Node suc, int n){//判断空格可否向该方向移动,,,表示空格向上向下向左向右移int i,flag = 0;for(i = 0;i < 9;i++)if(suc.data[i] == 0)break;switch(n){case 1:if(i/3 != 0)flag = 1;break;case 2:if(i/3 != 2)flag = 1;break;case 3:if(i%3 != 0)flag = 1;break;case 4:if(i%3 != 2)flag = 1;break;default:break;}return flag ;}void Spreadchild(Node * child,int n){//扩展child节点的字节点n表示方向,,,表示空格向上向下向左向右移int i,loc,temp;for(i = 0;i < 9;i++)child->data[i] = child->parent->data[i];for(i = 0;i < 9;i++)if(child->data[i] == 0)break;if(n==0)loc = i%3+(i/3 - 1)*3;else if(n==1)loc = i%3+(i/3 + 1)*3;else if(n==2)loc = i%3-1+(i/3)*3;elseloc = i%3+1+(i/3)*3;temp = child->data[loc];child->data[i] = temp;child->data[loc] = 0;}void Spread(Lnode * suc, Lstack * Open, Lstack * Closed, Node goal, float speed) {//扩展后继节点总函数int i;Node * child;for(i = 0; i < 4; i++){if(Canspread(**suc, i+1)) //判断某个方向上的子节点可否扩展{child = (Node *) malloc(sizeof(Node)); //扩展子节点 child->g = (*suc)->g +1; //算子节点的g值 child->parent = (*suc); //子节点父指针指向父节点Spreadchild(child, i); //向该方向移动空格生成子节点if(BelongProgram(&child, Open, Closed, goal, speed)) // 判断子节点是否属于OPEN或CLOSED表并作出相应的处理free(child);}}}///////////////////////扩展后继节点部分的函数-结束//////////////////////////////////Node * Process(Lnode * org, Lnode * goal, Lstack * Open, Lstack * Closed, float speed) {//总执行函数while(1){if((*Open)->next == NULL)return NULL; //判断OPEN表是否为空,为空则失败退出Node * minf = Minf(Open); //从OPEN表中取出f 值最小的节点Putinto(minf, Closed); //将节点放入CLOSED表中if(Equal(minf, *goal))return minf; //如果当前节点是目标节点,则成功退出Spread(&minf, Open, Closed, **goal, speed); //当前节点不是目标节点时扩展当前节点的后继节点}}int Shownum(Node * result){//递归显示从初始状态到达目标状态的移动方法if(result == NULL)return 0;else{int n = Shownum(result->parent);for(int i = 0; i < 3; i++){printf("\n");for(int j = 0; j < 3; j++){if(result->data[i*3+j] != 0)printf(" %d ",result->data[i*3+j]);else printf(" ");}}printf("\n");return n+1;}}void Checkinput(Node *suc){//检查输入int i = 0,j = 0,flag = 0;char c;while(i < 9){while(((c = getchar()) != 10)){if(c == ' '){if(flag >= 0)flag = 0;}else if(c >= '0' && c <= '8'){if(flag == 0){suc->data[i] = (c-'0');flag = 1;for(j =0; j < i; j++)if(suc->data[j] == suc->data[i])flag = -2;i++;}else if(flag >= 0)flag = -1;}elseif(flag >= 0)flag = -1;}if(flag <0 || i < 9){if(flag < 0){if(flag == -1)printf("含有非法字符或数字!\n请重新输入:\n");else if(flag == -2)printf("输入的数字有重复!\n请重新输入:\n");}else if(i < 9)printf("输入的有效数字不够!\n请重新输入:\n");i = 0;flag = 0;}}}void main(){//主函数 //初始操作,建立open和closed表Lstack Open = (Stack *) malloc(sizeof(Stack));Open->next = NULL;Lstack Closed = (Stack *) malloc(sizeof(Stack));Closed->next = NULL;Node * org = (Node *) malloc(sizeof(Node));org->parent = NULL; //初始状态节点org->f =1;org->g =1;Node * goal = (Node *) malloc(sizeof(Node)); //目标状态节点Node * result;float speed = 1;//speed搜索速度char c;printf("=================================\n");printf("说明:状态矩阵由0-8 九个数字表示,\n请依次按照九宫格上的行列顺序输入,每个数字间用空格隔开。

启发式搜索-A算法解决八数码问题

启发式搜索-A算法解决八数码问题

/*用启发式算法——A*搜索来解决八数码问题*/#include <stdio.h>#define MAX_BOARD 3*3#define MAX_DEPTH 22typedef struct BroadNode {int array[MAX_BOARD];int g;int h;int f;int depth;struct BroadNode *parent;}BNode, *BiNode;/*估计函数h(n)的计算,等于错置的图块数*/int evaluateBoard(BiNode T){int i, score;const int test[MAX_BOARD-1]={1,2,3,4,5,6,7,8};score = 0;for(i=0; i<MAX_BOARD-1; i++)score += (T->array[i] != test[i]);return score;}/*A*搜索,解决八数码问题*/void astarEightNumber(){int i;BiNode cur_board_p, child_p, temp;while(listEmpty(&openList_p)==false){/*从OPEN优先队列中,选取第一个,即f(n)最小的结点*/cur_board_p = getListBest(&openList_p);putList(&closedList_p, cur_board_p);if(cur_board_p->h == 0) /*h(n)==0,则表示找到了目标结点*/{/*输出路径过程,即从初始结点到目标结点路径上的每个结点*/showSolution(cur_board_p);return;}else{/*由于平均22步,就应该能找到解,故h(n)>22,则放弃该结点,继续查看其他的*/if(cur_board_p->depth > MAX_DEPTH)continue;/*列举从当前状态(结点)出发,所有可能的移动(子结点),最多4种移法*/for(i=0; i<4; i++){ /*找到下一个子结点*/child_p = getChildBoard(cur_board_p, i);if(child_p == (BiNode)0)continue;/*如果child_p在CLOSED表中,则抛弃child_p,继续循环*/if(onList(&closedList_p, child_p->array, NULL)){nodeFree(child_p);continue;}child_p->depth = cur_board_p->depth+1;child_p->h = evaluateBoard(child_p);child_p->g = child_p->depth;child_p->f = child_p->h + child_p->g;/*如果child_p在OPEN表上,则*/if(onList(&openList_p, child_p->array, NULL)) {temp = getList(&openLisy_p, child_p->array);if(temp->f < child_p->f) {nodeFree(child_p);putList(&openList_p, temp);continue;}nodeFree(temp);child_p->parent = cur_board_p;putList(&openList_p, child_p);}else {/*child_p既不在CLOSED表上,也不在OPEN表上,则将其插入OPEN 表即可*/child_p->parent = cur_board_p;putList(&openList_p, child_p);}}}};}。

八数码问题求解与界面设计

八数码问题求解与界面设计

基于启发式搜索的九宫图问题求解及其界面设计一、九宫图问题简介九宫图问题又称八数码问题,在3×3的九宫格棋盘上,每一个将牌都刻有1—8中的某一个数码。

棋盘中留有一个空格,允许其周围的某一个将牌向空格移动,这样通过移动将牌就可以不断改变将牌的布局。

给定一种初始的将牌布局(称初始状态)和一个目标布局(称目标状态),如何移动将牌,实现从初始状态到目标状态的转变。

问题的解答也就是给出一个合法的走步序列。

二、求解算法简介1.解的存在性分析九宫图问题的状态空间共有9!个状态,对于给定的初始状态和目标状态,有9!/2个状态要搜索,九宫图问题不一定能进行求解,因此对于给定的初始状态和目标状态需首先进行解存在性判断。

引入线性代数中逆序数的概念:在一个排列中,如果一对数的前后位置和大小顺序相反,即前面的数大于后面的数,那么它们就成为一个逆序,排列中逆序的总数就称为这个排列的逆序数。

只有当初始状态的排列逆序数与目标状态的排列逆序数奇偶性相同时,初始状态才能通过移动到达目标状态。

2.A*算法简介本文选用A*算法进行九宫图问题求解。

A*算法是一种静态路网中求解最短路径最有效的直接搜索方法。

A*算法结合了深度优先算法和广度优先,是一种启发式搜索算法。

评估函数为:f(n)=g(n)+h(n),f(n)表示从初始状态经由状态n 到目标状态的估计,g(n)是在状态空间中从初始状态到状态n 的实际代价,h(n)是从状态n 到目标状态的最佳路径的估计代价,本文算法中g(n)为从初始状态到当前状态的深度,h (n )为节点n 的每一数码与其目标位置之间的曼哈顿距离之和,可表示为:81()(||||)ni ti ni ti i h n x x y y ==-+-∑其中ni x 为n 状态下数字i 所在的行,ti x 为目标状态数字i 所在行,ni y 表示n 状态下数字i 所在列,ti y 表示目标状态数字i 所在列。

3.搜索动作空间简介空格移动空间为{上、下、左、右},但并非所有位置均能进行全空间搜索,判断空格所在位置,根据其所在位置对其搜索动作进行限制,其动作空间如表1所示。

启发式搜索算法解决八数码问题(C语言)

启发式搜索算法解决八数码问题(C语言)

1、程序源代码#include <stdio.h>#include<malloc.h>struct node{int a[3][3];//用二维数组存放8数码int hx;//函数h(x)的值,表示与目标状态的差距struct node *parent;//指向父结点的指针struct node *next;//指向链表中下一个结点的指针};//------------------hx函数-------------------//int hx(int s[3][3]){//函数说明:计算s与目标状态的差距值int i,j;int hx=0;int sg[3][3]={1,2,3,8,0,4,7,6,5};for(i=0;i<3;i++)for(j=0;j<3;j++)if(s[i][j]!=sg[i][j])hx++;return hx;}//-------------hx函数end----------------------////-------------extend扩展函数----------------//struct node *extend(node *ex){ //函数说明:扩展ex指向的结点,并将扩展所得结点组成一条//单链表,head指向该链表首结点,并且作为返回值int i,j,m,n; //循环变量int t; //临时替换变量int flag=0;int x[3][3];//临时存放二维数组struct node *p,*q,*head;head=(node *)malloc(sizeof(node));//headp=head;q=head;head->next=NULL;//初始化for(i=0;i<3;i++)//找到二维数组中0的位置{for(j=0;j<3;j++)if(ex->a[i][j]==0){flag=1;break;}if(flag==1)break;}for(m=0;m<3;m++)//将ex->a赋给xfor(n=0;n<3;n++)x[m][n]=ex->a[m][n];//根据0的位置的不同,对x进行相应的变换//情况1if(i-1>=0){t=x[i][j];x[i][j]=x[i-1][j];x[i-1][j]=t;flag=0;for(m=0;m<3;m++)//将x赋给afor(n=0;n<3;n++)if(x[m][n]==ex->parent->a[m][n])flag++;if(flag!=9){q=(node *)malloc(sizeof(node));for(m=0;m<3;m++)//将x赋给afor(n=0;n<3;n++)q->a[m][n]=x[m][n];q->parent=ex;q->hx=hx(q->a);q->next=NULL;p->next=q;p=p->next;}}//情况2for(m=0;m<3;m++)//将ex->a重新赋给x,即还原x for(n=0;n<3;n++)x[m][n]=ex->a[m][n];if(i+1<=2){t=x[i][j];x[i][j]=x[i+1][j];x[i+1][j]=t; flag=0;for(m=0;m<3;m++)for(n=0;n<3;n++)if(x[m][n]==ex->parent->a[m][n])flag++;if(flag!=9){q=(node *)malloc(sizeof(node));for(m=0;m<3;m++)//将x赋给afor(n=0;n<3;n++)q->a[m][n]=x[m][n];q->parent=ex;q->hx=hx(q->a);q->next=NULL;p->next=q;p=p->next;}}//情况3for(m=0;m<3;m++)//将ex->a重新赋给x,即还原x for(n=0;n<3;n++)x[m][n]=ex->a[m][n];if(j-1>=0){t=x[i][j];x[i][j]=x[i][j-1];x[i][j-1]=t;flag=0;for(m=0;m<3;m++)for(n=0;n<3;n++)if(x[m][n]==ex->parent->a[m][n])flag++;if(flag!=9){q=(node *)malloc(sizeof(node));for(m=0;m<3;m++)//将x赋给afor(n=0;n<3;n++)q->a[m][n]=x[m][n];q->parent=ex;q->hx=hx(q->a);q->next=NULL;p->next=q;p=p->next;}}//情况4for(m=0;m<3;m++)//将ex->a重新赋给x,即还原xfor(n=0;n<3;n++)x[m][n]=ex->a[m][n];if(j+1<=2){t=x[i][j];x[i][j]=x[i][j+1];x[i][j+1]=t;flag=0;for(m=0;m<3;m++)for(n=0;n<3;n++)if(x[m][n]==ex->parent->a[m][n])flag++;if(flag!=9){q=(node *)malloc(sizeof(node));for(m=0;m<3;m++)for(n=0;n<3;n++)q->a[m][n]=x[m][n];q->parent=ex;q->hx=hx(q->a);q->next=NULL;p->next=q;p=p->next;}}head=head->next;return head;}//---------------extend函数end-----------------------////----------------insert函数-------------------------//node* insert(node *open,node * head){ //函数说明:将head链表的结点依次插入到open链表相应的位置, //使open表中的结点按从小到大排序。

图搜索与问题求解(八数码)

图搜索与问题求解(八数码)

图搜索与问题求解实验报告一实验题目图搜索与问题求解二实验目的1熟悉和掌握启发式搜索/A*搜索的定义、估价函数和算法过程;2 理解和掌握搜索过程,能够用选定的编程语言求解八数码问题,理解求解流程和搜索顺序;3 比较并分析图搜索策略的实质,通过实验理解启发式搜索/A*搜索的意义。

三实验要求1以九宫问题/八数码问题为例,以某种启发式搜索/A*搜索策略编程演示其搜索过程;2 定义启发式函数,能正确求解出从初始状态到目标状态的移动路线;3 对不可达状态能进行正确识别;4对所采用的启发式函数做出性能分析。

四数据结构typedef struct Qnode{ //队列的节点类型定义long a; //将8数码转化为长整型后入队列int dnum; //与目标状态数码不同的位置的个数Qnode *next;}*QueuePtr;typedef struct{QueuePtr front; //队头指针QueuePtr rear; //队尾指针}LinkQueue; //链式队列五实验算法1 说明有解和无解如何判定;int NiXu(int a[][3]) //求出所给状态的逆序数{i nt i,j,k=0,sum=0;i nt b[8];f or(i=0;i<3;i++)for(j=0;j<3;j++)if(a[i][j]) //空格用0代替,逆序不计空格b[k++]=a[i][j];for(i=1;i<8;i++)for(j=0;j<i;j++)if(b[i]<b[j])sum++;return sum;}if(NiXu(start)%2 != NiXu(end)%2)printf("无法到达!\n");e lse{printf("广度优先搜索如下:\n\n");search();}2 说明启发式函数如何设定;int h(long x){i nt sum=0;i nt b[3][3];u_trans(x,b);f or (int i=0;i<3;i++)for (int j=0;j<3;j++)if (end[i][j]!=b[i][j])sum++;r eturn sum;}3说明实验中采用的搜索算法。

人工智能:八数码难题的启发式搜索

人工智能:八数码难题的启发式搜索

人工智能基础大作业----八数码难题学院:数学与计算机科学学院2016.12.20一、实验名称八数码难题的启发式搜索二、实验目的八数码问题:在3×3的方格棋盘上,摆放着1到8这八个数码,有1个方格是空的,其初始状态如图1所示,要求对空格执行空格左移、空格右移、空格上移和空格下移这四个操作使得棋盘从初始状态到目标状态。

要求:1.熟悉人工智能系统中的问题求解过程;2.熟悉状态空间的启发式搜索算法的应用;3.熟悉对八数码问题的建模、求解及编程语言的应用。

三、实验设备及软件环境1.实验编程工具:VC++ 6.02.实验环境:Windows7 64位四、实验方法:启发式搜索1.算法描述1.将S放入open表,计算估价函数f(s)2.判断open表是否为空,若为空则搜索失败,否则,将open表中的第一个元素加入close表并对其进行扩展(每次扩展后加入open表中的元素按照代价的大小从小到大排序,找到代价最小的节点进行扩展)注:代价的计算公式f(n)=d(n)+w(n).其中f(n)为总代价,d(n)为节点的度,w(n)用来计算节点中错放棋子的个数。

判断i是否为目标节点,是则成功,否则拓展i,计算后续节点f(j),利用f(j)对open表重新排序2.算法流程图:3.程序源代码:# include<stdio.h># include<string.h># include<malloc.h># include<stdlib.h>typedef struct node {int i,cost,degree,exp,father;int a[3][3];struct node *bef,*late;struct node *son;}treenode;int flag=0,count=1,num=0,i=0;void set(treenode *s);void cpynode(treenode *s1,treenode *s2);void add1(treenode *s,treenode *open);void adjust1(treenode *close);void jscost(treenode *s);void tiaozheng(treenode *open);void sortopen(treenode *open);int test(treenode *s1,treenode *s2);void position(treenode *s,treenode *open,treenode*close,treenode *s1);void printstr(treenode *open);int search(treenode *s1,treenode *s2);void input(treenode *s);int cmpnode(treenode *s1,treenode *s2);void print(treenode *s);void add(treenode *s,treenode *close);void xuhao(treenode *s);void extend(treenode *r1,treenode *s,treenode *s1,treenode *open,treenode *close);void main() {treenode *s0,*s1,*s;treenode *open,*close,*opend,*closed;open=(treenode*)malloc(sizeof(treenode));close=(treenode*)malloc(sizeof(treenode));open->late=NULL;close->late=NULL;opend=open;closed=close;s0=(treenode*)malloc(sizeof(treenode));set (s0);s1=(treenode*)malloc(sizeof(treenode));set(s1);printf("请输入八数码的初始状态:(以空格为分隔)\n");input (s0);printf("请输入八数码的目标状态:(以空格为分隔)\n");input(s1);xuhao(s0);add (s0,opend);while(open->late!=NULL && flag==0) {s=(treenode*)malloc(sizeof(treenode));cpynode(s,open->late);open=open->late;add(s,close);if(test(s,s1)==0){flag=1; }else{position(s,open,close,s1);sortopen(open); };};if(open->late!=NULL) {printf("搜索过程如下:\n ");adjust1(close);printstr(close);printf("\n%d 步,%d 个节点\n",num,count);} else {printf("查找错误 ! \n");}; }void set(treenode *s) {s->i=i;s->father=0;s->degree=0;s->bef=NULL;s->son=NULL;s->late=NULL; };void input(treenode *s) {int j,k;for(j=0;j<3;j++)for(k=0;k<3;k++)scanf("%d",&s->a[j][k]); };int cmpnode(treenode *s1,treenode *s2){ int j,k;for(j=0;j<3;j++)for(k=0;k<3;k++) {if(s1->a[j][k]!=s2->a[j][k])return 0; };return 1; }int test(treenode *s1,treenode *s2) { int j,k,n=0;for(j=0;j<3;j++)for(k=0;k<3;k++) {if(s1->a[j][k]!=s2->a[j][k])n++; };s1->exp=n;return n; };void xuhao(treenode *s) {i++;s->i=i; }void cpynode(treenode *s1,treenode *s2) { int j,k;for(j=0;j<3;j++)for(k=0;k<3;k++)s1->a[j][k]=s2->a[j][k];s1->bef=s2->bef;s1->cost=s2->cost;s1->exp=s2->exp;s1->degree=s2->degree;s1->i=s2->i;s1->father=s2->father; };void print(treenode *s) {int j,k;for(j=0;j<3;j++) {for(k=0;k<3;k++) {printf("%2d",s->a[j][k]); }if(j==1) printf(" n=%2d d=%2df=%2d",s->i,s->degree,s->father);printf("\n"); }printf("\n"); }void position(treenode *s,treenode *open,treenode *close,treenode *s1) {int m,n,t,k;treenode *r1;for(m=0;m<3;m++) {for(n=0;n<3;n++) {k=s->a[m][n];if(k==0)break; };if(k==0) break; }if(m+1<=2&&flag==0) {r1=(treenode*)malloc(sizeof(treenode));cpynode(r1,s);t=r1->a[m+1][n];r1->a[m+1][n] = r1->a[m][n];r1->a[m][n]=t;extend(r1,s,s1,open,close); };if(m-1>=0&&flag==0) {r1=(treenode*)malloc(sizeof(treenode));cpynode(r1,s);t=r1->a[m-1][n];r1->a[m-1][n]=r1->a[m][n];r1->a[m][n]=t;extend(r1,s,s1,open,close); };if(n-1>=0 && flag==0) {r1=(treenode*)malloc(sizeof(treenode));cpynode(r1,s);t=r1->a[m][n-1];r1->a[m][n-1]=r1->a[m][n];r1->a[m][n]=t;extend(r1,s,s1,open,close); };if(n+1<=2 && flag==0) {r1=(treenode*)malloc(sizeof(treenode));cpynode(r1,s);t=r1->a[m][n+1];r1->a[m][n+1]=r1->a[m][n];r1->a[m][n]=t;extend(r1,s,s1,open,close); }; }void printstr(treenode *s) {treenode *t;t=s->late;while(t!=NULL) {num++;print(t);t=t->son; }; }void extend(treenode *r1,treenode *s,treenode *s1,treenode *open,treenode *close) {r1->father=s->i;r1->degree=s->degree+1;if(test(r1,s1)!=0) {jscost(r1);if(search(r1,close)==1 && search(r1,open)==1) { xuhao(r1);add1(r1,open);r1->bef=s;count++; }else free(r1); }else {xuhao(r1);jscost(r1);count++;add(r1,close);r1->bef=s;flag=1; } }int search(treenode *s1,treenode *close) { treenode *r,*t;r=s1;t=close->late;while(t!=NULL) {if(r->exp==t->exp) {if(cmpnode(r,t)==1)return 0; };t=t->late; };return 1; }void add(treenode *s,treenode *close) { treenode *r,*t;t=s;r=close;while(r->late!=NULL)r=r->late;r->late=t;t->late=NULL; }void add1(treenode *s,treenode *open){ treenode *t;t=open;s->late=t->late;t->late=s; }void adjust1(treenode *close) {treenode *s,*t;s=close;s->late->bef=NULL;while(s->late!=NULL)s=s->late;s->son=NULL;while(s->bef!=NULL) {t=s->bef;t->son=s;s=s->bef; }; }void jscost(treenode *s) {s->cost=(s->exp)+s->degree; }void sortopen(treenode *open) {treenode *t,*s,*r;int k;r=(treenode*)malloc(sizeof(treenode));t=open->late;while(t!=NULL && t->late!=NULL) {s=t->late;k=t->cost;while(s!=NULL) {if(k > s->cost) {k=s->cost;cpynode(r,t);cpynode(t,s);cpynode(s,r); }s=s->late; }t=t->late; }; }五、实验结果:1.程序截图2.搜索过程请输入八数码的初始状态:(以空格为分隔)2 8 31 0 47 6 5请输入八数码的目标状态:(以空格为分隔)1 2 38 0 47 6 5搜索过程如下:2 8 31 0 4 n= 1 d= 0 f= 07 6 52 0 31 8 4 n= 3 d= 1 f= 17 6 50 2 31 8 4 n= 8 d=2 f= 37 6 51 2 30 8 4 n=10 d= 3 f= 87 6 51 2 38 0 4 n=12 d= 4 f=107 6 55 步,12 个节点Press any key to continue六、实验分析:在进行搜索的过程中,同时记录了扩展新节点的个数。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

启发式搜索1. 介绍八数码问题也称为九宫问题。

在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。

棋盘上还有一个空格(以数字0来表示),与空格相邻的棋子可以移到空格中。

要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。

所谓问题的一个状态就是棋子在棋盘上的一种摆法。

解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态。

2. 使用启发式搜索算法求解8数码问题。

1) A ,A 星算法采用估价函数()()()()w n f n d n p n ⎧⎪=+⎨⎪⎩, 其中:()d n 是搜索树中结点n 的深度;()w n 为结点n 的数据库中错放的棋子个数;()p n 为结点n 的数据库中每个棋子与其目标位置之间的距离总和。

2)宽度搜索采用f(i)为i 的深度,深度搜索采用f(i)为i 的深度的倒数。

3. 算法流程① 把起始节点S 放到OPEN 表中,并计算节点S 的)(S f ;② 如果OPEN 是空表,则失败退出,无解;③ 从OPEN 表中选择一个f 值最小的节点i 。

如果有几个节点值相同,当其中有一个 为目标节点时,则选择此目标节点;否则就选择其中任一个节点作为节点i ;④ 把节点i 从 OPEN 表中移出,并把它放入 CLOSED 的已扩展节点表中;⑤ 如果i 是个目标节点,则成功退出,求得一个解;⑥ 扩展节点i ,生成其全部后继节点。

对于i 的每一个后继节点j :计算)(j f ;如果j 既不在OPEN 表中,又不在CLOCED 表中,则用估价函数f 把它添入OPEN 表中。

从j 加一指向其父节点i 的指针,以便一旦找到目标节点时记住一个解答路径;如果j 已在OPEN 表或CLOSED 表中,则比较刚刚对j 计算过的f 和前面计算过的该节点在表中的f 值。

如果新的f 较小,则(I)以此新值取代旧值。

(II)从j 指向i ,而不是指向他的父节点。

(III)如果节点j 在CLOSED 表中,则把它移回OPEN 表中。

⑦ 转向②,即GOTO ②。

4. 估价函数计算一个节点的估价函数,可以分成两个部分:1、 已经付出的代价(起始节点到当前节点);2、 将要付出的代价(当前节点到目标节点)。

节点n 的估价函数)(n f 定义为从初始节点、经过n 、到达目标节点的路径的最小代价的估计值,即)(*n f = )(*n g + )(*n h 。

)(*n g 是从初始节点到达当前节点n 的实际代价;)(*n h 是从节点n 到目标节点的最佳路径的估计代价,体现出搜索过程中采用的启发式信息(背景知识),称之为启发函数。

)(*n g 所占的比重越大,越趋向于宽度优先或等代价搜索;反之,)(*n h 的比重越大,表示启发性能就越强。

5. 实验代码为方便起见,目标棋局为不变(1)以下代码估价函数为深度+错放棋子个数 (2) 若估价函数为深度+每个棋子与其目标位置之间的距离总和,则加入估价函数int calvalue1(int a[]) //不在位棋子数{int c = 0;int b=0;for (int i = 0;i <= 8;i++)for (int j = 0;j <= 8;j++)if (a[i] = goal[j])if (goal[j] != 0)c=c+abs(i%3-j%3)+abs((i- i%3)/3+(j- j%3)/3);return c;}(3)宽度搜索采用OPEN->jiedian.f = depth;(4) 深度搜索采用OPEN->jiedian.f = -depth;源代码:1. #include "stdio.h"2.3. int goal[9] = { 1,2,3,8,0,4,7,6,5 }, sgoal[9];//goal 为棋盘的目标布局,并用中间状态sgoal与之比较4.5.struct Board6.{7.int shuzu[9];8.int d, f, e;//d:深度;f:启发函数;e:记录前一次的扩展节点9.};10.11.struct NodeLink12.{13.Board jiedian;14.NodeLink *parent;15.NodeLink *previous;16.NodeLink *next;17.NodeLink *path;18.};19.//更新纪录八数码的状态20.void setboard(int a[], int b[], int flag) //flag=0,写棋子;flag=1,写棋盘21.{22.for (int i = 0;i <= 8;i++)23.if (flag)24.a[b[i]] = i;25.else26.b[a[i]] = i;27.}28.//计算启发值的函数29.int calvalue(int a[]) //不在位棋子数30.{31.int c = 0;32.for (int i = 0;i <= 8;i++)33.if (a[i] != goal[i])34.if (goal[i] != 0)35.c++;36.return c;37.}38.//生成一个新节点的函数39.NodeLink *newnode(NodeLink *TEM, int depth, int flag)40.{41.NodeLink *temp = new NodeLink;42.for (int i = 0;i <= 8;i++)43.temp->jiedian.shuzu[i] = TEM->jiedian.shuzu[i];44.switch (flag)45.{46.case 1:47.{48.temp->jiedian.shuzu[0]--;49.temp->jiedian.shuzu[sgoal[temp->jiedian.shuzu[0]]]++; //向左移50.break;51.}52.case 2:53.{54.temp->jiedian.shuzu[0]++;55.temp->jiedian.shuzu[sgoal[temp->jiedian.shuzu[0]]]--; //向右移56.break;57.}58.case 3:59.{60.temp->jiedian.shuzu[0] -= 3;61.temp->jiedian.shuzu[sgoal[temp->jiedian.shuzu[0]]] += 3; //向上移62.break;63.}64.case 4:65.{66.temp->jiedian.shuzu[0] += 3;67.temp->jiedian.shuzu[sgoal[temp->jiedian.shuzu[0]]] -= 3; //向下移68.break;69.}70.}71.temp->jiedian.d = depth + 1;72.setboard(sgoal, temp->jiedian.shuzu, 1);73.temp->jiedian.f = temp->jiedian.d + calvalue(sgoal);74.temp->jiedian.e = flag;75.temp->parent = TEM;76.return temp;77.}78.//把新节点加入OPEN队列79.NodeLink *addnode(NodeLink *head, NodeLink *node) //把node插入到head链中80.{81.NodeLink *TEM;82.TEM = head;83.head = node;84.head->next = TEM;85.head->previous = NULL;86.if (TEM)87.TEM->previous = head; //TEM已为空,无需操作88.return head;89.}90.91.//求启发值最小的结点92.NodeLink *minf(NodeLink *head)93.{94.NodeLink *min, *forward;95.min = head;96.forward = head;97.while (forward)98.{99.if (min->jiedian.f>forward->jiedian.f)100.min = forward;101.forward = forward->next;102.}103.return min;104.}105.106.int main()107.{108.int depth = 0;109.int source[9];110.int i, j;111.112.NodeLink *OPEN = new NodeLink;113.NodeLink *TEMP, *TEM;114.115.printf("请输入初始状态:\n");116.for (i = 0;i<9;i++)117.scanf_s("%d", &source[i]);118.119.setboard(source, OPEN->jiedian.shuzu, 0);120.OPEN->jiedian.d = depth;121.OPEN->jiedian.e = 0;122.OPEN->jiedian.f = depth + calvalue(source);123.OPEN->next = NULL;124.OPEN->previous = NULL;125.OPEN->parent = NULL;126.127.while (OPEN)128.{129.TEMP = minf(OPEN); //求具有最小启发值的节点130.setboard(sgoal, TEMP->jiedian.shuzu, 1); //写棋盘131.if (!calvalue(sgoal))132.break;133.if (TEMP != OPEN) //如果不是第一个节点134.{135.TEMP->previous->next = TEMP->next;136.TEMP->next->previous = TEMP->previous;137.}138.else //是第一个节点139.{140.if (OPEN->next) //如果还有节点141.{142.OPEN = OPEN->next;143.OPEN->previous = NULL;144.}145.else OPEN = NULL; //否则置为空146.}147.148.if (TEMP->jiedian.shuzu[0] - 1 >= 0 && TEMP->jiedian.e != 2) //防止棋子回到原状态149.OPEN = addnode(OPEN, newnode(TEMP, depth, 1));150.if (TEMP->jiedian.shuzu[0] + 1 <= 8 && TEMP->jiedian.e != 1)151.OPEN = addnode(OPEN, newnode(TEMP, depth, 2));152.if (TEMP->jiedian.shuzu[0] - 3 >= 0 && TEMP->jiedian.e != 4)153.OPEN = addnode(OPEN, newnode(TEMP, depth, 3));154.if (TEMP->jiedian.shuzu[0] + 3 <= 8 && TEMP->jiedian.e != 3)155.OPEN = addnode(OPEN, newnode(TEMP, depth, 4));156.depth++;157.}158.159.if (OPEN) //如有解,则打印出解的步骤160.{161.TEMP->path = NULL;162.while (TEMP->parent) //每次回溯父节点,生成路径163.{164.TEMP->parent->path = TEMP;165.TEMP = TEMP->parent;166.}167.j = 0;168.while (TEMP->path)169.{170.setboard(sgoal, TEMP->jiedian.shuzu, 1);171.printf("第%d步:\n", j);172.for (i = 0;i <= 2;i++)173.printf(" %d", sgoal[i]);174.printf(" \n");175.for (i = 3;i <= 5;i++)176.printf(" %d", sgoal[i]);177.printf("\n");178.for (i = 6;i <= 8;i++)179.printf(" %d", sgoal[i]);180.printf("\n");181.TEMP = TEMP->path;182.j++;183.}184.setboard(sgoal, TEMP->jiedian.shuzu, 1);185.printf("第%d步:\n", j);186.for (i = 0;i <= 2;i++)187.printf(" %d", sgoal[i]);188.printf("\n");189.for (i = 3;i <= 5;i++)190.printf(" %d", sgoal[i]);191.printf("\n");192.for (i = 6;i <= 8;i++)193.printf(" %d", sgoal[i]);194.printf("\n");195.}196.else197.printf("无法求解!");198.}(1)以上代码估价函数为深度+错放棋子个数(2) 若估价函数为深度+每个棋子与其目标位置之间的距离总和,则函数改为int calvalue(int a[]) //不在位棋子数{int c = 0;int b=0;for (int i = 0;i <= 8;i++)for (int j = 0;j <= 8;j++)if (a[i] = goal[j])if (goal[j] != 0)c=c+abs(i%3-j%3)+abs((i- i%3)/3+(j- j%3)/3);return c;}(3)宽度搜索采用OPEN->jiedian.f = depth;(4) 深度搜索采用OPEN->jiedian.f = -depth;6.输出结果:(输入为:)目标状态为:(1)估价函数为深度+错放棋子个数(2) 估价函数为深度+每个3棋子与其目标位置之间的距离总和(3)宽度搜索采用OPEN->jiedian.f = depth;(4) 深度搜索采用OPEN->jiedian.f = -depth;精品文档。

相关文档
最新文档