当前位置:文档之家› 实验一 启发式搜索算法

实验一 启发式搜索算法

实验一启发式搜索算法学号:**********班级:计科二班姓名:***一、实验内容:使用启发式搜索算法求解8数码问题。

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

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

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

三、实验原理:(一)问题描述在一个3*3的方棋盘上放置着1,2,3,4,5,6,7,8八个数码,每个数码占一格,且有一个空格。

这些数码可以在棋盘上移动,其移动规则是:与空格相邻的数码方格可以移入空格。

现在的问题是:对于指定的初始棋局和目标棋局,给出数码的移动序列。

该问题称八数码难题或者重排九宫问题。

(二)问题分析八数码问题是个典型的状态图搜索问题。

搜索方式有两种基本的方式,即树式搜索和线式搜索。

搜索策略大体有盲目搜索和启发式搜索两大类。

盲目搜索就是无“向导”的搜索,启发式搜索就是有“向导”的搜索。

启发式搜索:由于时间和空间资源的限制,穷举法只能解决一些状态空间很小的简单问题,而对于那些大状态空间的问题,穷举法就不能胜任,往往会导致“组合爆炸”。

所以引入启发式搜索策略。

启发式搜索就是利用启发性信息进行制导的搜索。

它有利于快速找到问题的解。

由八数码问题的部分状态图可以看出,从初始节点开始,在通向目标节点的路径上,各节点的数码格局同目标节点相比较,其数码不同的位置个数在逐渐减少,最后为零。

所以,这个数码不同的位置个数便是标志一个节点到目标节点距离远近的一个启发性信息,利用这个信息就可以指导搜索。

即可以利用启发信息来扩展节点的选择,减少搜索范围,提高搜索速度。

启发函数设定。

对于八数码问题,可以利用棋局差距作为一个度量。

搜索过程中,差距会逐渐减少,最终为零,为零即搜索完成,得到目标棋局。

四、实验程序及其说明:1)int goal[N][N],struct Chessboard :试验中我定义goal 数组为目标状态——{1,2,3,8,0,4,7,6,5}。

结构体Chessboard 记录棋盘信息,其中变量pos 数组坐标记录每个数码a 的位置,其值为数码a 。

d 表示该棋盘的深度,f 为启发函数值,move 为父节点移动到该节点的方式,以便在输出时告诉用户该如何移动棋子知道目标状态。

2)struct LNode :定义节点LNode 结构体,存放该节点状态时的棋盘信息board ,和指向父节点、下一节点的指针(*parent,*next),以及标记量flag ——值为true 时表示该节点是最佳路径上的节点。

3)int* Findzero(LNode* &Node):为方便找到空格,我设计了找到该函数Findzero(*&Node),以便找到当前节点空格所在位置以利于接下来的程序执行。

返回值为空格所在的行和列。

4)int Wrong(LNode *Node)和int pick(LNode *Node):分别为函数)(n ω和)(n p 。

5)LNode* extend(LNode *Node,int depth,int zero[2],int moveflag,int Choose)树形方式扩展节点。

Node 为要扩展的当前节点,depth 为当前深度,zero 存放该节点空格位置信息,moveflag 即扩展节点的移动方式,Choose 为选择函数)(n ω还是)(n p 。

6)void InitList(LNode* &Open,LNode* &Close)和int ListInsert(List &L,LNode* NewNode)分别为表OPEN 、CLOSE 的创建和表的插入操作。

7)LNode* Getminf(List &L)主要是实现从OPEN 表中选择一个f 值最小的节点i 。

如果有几个节点值相同,当其中 有一个为目标节点时,则选择此目标节点;否则就选择其中任一个节点作为节点i 。

五、实验源代码#include<stdio.h>#include<malloc.h>#include<stdlib.h>#define Overflow 1#define N 3int goal[N][N]={1,2,3,8,0,4,7,6,5};int zero[2],NodeQTY=0;int *z=zero;//记录0的位置,zero[0]:r 行;zero[1]:c 列typedef int Piece;struct Chessboard{//棋盘信息Piece pos[N][N];//记录每个数码a的位置r行c列int d,f,move;//d:深度;f:启发函数值;move:父节点移动到该节点的方式};struct LNode{Chessboard board;LNode *parent,*next;bool flag;};typedef LNode* List;int* Findzero(LNode* &Node){int i,j,zr[2];int *z=zr;for(i=0;i<N;i++){for(j=0;j<N;j++){if(Node->board.pos[i][j]==0){zr[0]=i+1;zr[1]=j+1;break;}}}return z;}int Wrong(LNode *Node){int w=0,i,j;for(i=0;i<N;i++){for(j=0;j<N;j++){if(Node->board.pos[i][j]!=goal[i][j]&&Node->board.pos[i][j]!=0)w++;}}return w;}int pick(LNode *Node){int w=0,i,j,ii,jj;for(i=0;i<N;i++){for(j=0;j<N;j++){if(Node->board.pos[i][j]!=goal[i][j]&&Node->board.pos[i][j]!=0){ for(ii=0;ii<N;ii++)for(jj=0;jj<N;jj++)if(Node->board.pos[i][j]==goal[ii][jj]){w=w+abs(ii-i)+abs(jj-j);break;}}}}return w;}LNode* extend(LNode *Node,int depth,int zero[2],int moveflag,int Choose){LNode* NewNode=new LNode;for(int i=0;i<N;i++){for(int j=0;j<N;j++){NewNode->board.pos[i][j]=Node->board.pos[i][j];}}switch(moveflag){case 1: //向左移,不能出界:zero[1]>=2New-Node->board.pos[zero[0]-1][zero[1]-1]=NewNode->board.pos[zero[0]-1][zero[1]-2];NewNode->board.pos[zero[0]-1][zero[1]-2]=0;break;case 2: //向右移,不能出界:zero[1]<=2New-Node->board.pos[zero[0]-1][zero[1]-1]=NewNode->board.pos[zero[0]-1][zero[1]];NewNode->board.pos[zero[0]-1][zero[1]]=0;break;case 3: //向上移,不能出界:zero[0]>=2New-Node->board.pos[zero[0]-1][zero[1]-1]=NewNode->board.pos[zero[0]-2][zero[1]-1];NewNode->board.pos[zero[0]-2][zero[1]-1]=0;break;case 4: //向下移,不能出界:zero[0]<=2New-Node->board.pos[zero[0]-1][zero[1]-1]=NewNode->board.pos[zero[0]][zero[1]-1];NewNode->board.pos[zero[0]][zero[1]-1]=0;break;}NewNode->board.d=depth+1;switch(Choose){case 1:NewNode->board.f=NewNode->board.d+Wrong(NewNode);break;case 2:NewNode->board.f=NewNode->board.d+pick(NewNode);break;}NewNode->board.move=moveflag;NewNode->parent=Node;NodeQTY++;return NewNode;}void InitList(LNode* &Open,LNode* &Close){Open=(List)malloc(sizeof(LNode));Close=(List)malloc(sizeof(LNode));if(!Open&&!Close)exit(Overflow);Open->next=NULL;Close->next=NULL;}int ListInsert(List &L,LNode* NewNode){List p=L;while(p->next){p=p->next;}NewNode->next=p->next;p->next=NewNode;return true;}LNode* Getminf(List &L){List p=L,q=L->next,r=L,min;min=q;//p,q寻找f最小值的指针,r指向表L中min前一个元素if(!q)return NULL;while(q){if(min->board.f>q->board.f){r=p;min=q;}p=q;q=q->next;}r->next=min->next;min->next=NULL;return min;}int main(){int i,j,choose;List Open,Close;LNode *Best,*current;LNode *Start=new LNode;printf("\t\t\t八数码问题求解\n");printf("\n请输入初始状态:");for(i=0;i<N;i++){for(j=0;j<N;j++){scanf("%d",&(Start->board.pos[i][j]));}}printf("(注:The flag of movement--1:左移;2:右移;3:上移;4:下移)\n"); printf("初始棋盘状态:\n");for(i=0;i<N;i++){for(j=0;j<N;j++){printf("|%d",Start->board.pos[i][j]);}printf("|\n");}InitList(Open,Close);printf("请选择(1:A算法;2:A*算法):");scanf("%d",&choose);Start->board.d=0;switch(choose){case 1:Start->board.f=Start->board.d+Wrong(Start);break;case 2:Start->board.f=Start->board.d+pick(Start);break;} // Start->board.f=0+Wrong(Start);Start->board.move=0;Start->parent=NULL;Start->next=NULL;Start->flag=1;ListInsert(Open,Start);//将S加入到Open表中while(Open->next){Best=Getminf(Open);ListInsert(Close,Best);if(!(Best->board.f-Best->board.d)){printf("$$$******有解!******$$$\n");break;}z=Findzero(Best);zero[0]=*(z+0);zero[1]=*(z+1);if(zero[1]>=N-1&&Best->board.move!=2)ListInsert(Open,extend(Best,Best->board.d,zero,1,choose));if(zero[1]<=N-1&&Best->board.move!=1)ListInsert(Open,extend(Best,Best->board.d,zero,2,choose));if(zero[0]>=N-1&&Best->board.move!=4)ListInsert(Open,extend(Best,Best->board.d,zero,3,choose));if(zero[0]<=N-1&&Best->board.move!=3)ListInsert(Open,extend(Best,Best->board.d,zero,4,choose));}printf("本算法搜索图中总共扩展的节点数为:%d\n",NodeQTY);printf("\t最佳路径如下所示:\n");if(Open->next){while(Best->parent){Best->flag=1;Best=Best->parent;}List p=Close->next,q=Close->next;if(p==Start) q=p->next;else exit(1);int Step=0;while(p&&q)//在Close表中依标记找到路径{if(q->flag==1&&q->parent==p){printf("Step %d:0 move asthe %d-flag of movement.\n",++Step,q->board.move);for(i=0;i<N;i++){for(j=0;j<N;j++){printf("|%d",q->board.pos[i][j]);}printf("|\n");}p=q;//记住父节点}q=q->next;}printf("到达目标状态!\n");}else printf("该问题无法求解!\n");}六、实验总结通过本次实验,让我更加了解启发式搜索算法的原理,见识了其广泛的应用;同时加强了本人阅读程序能力和编程能力,以及如何将理论问题解决实际应用的能力。

相关主题