深度宽度优先搜索 - 八数码
C语言实现8数码问题

1、实验目的(1)熟悉人工智能系统中的问题求解过程;(2)熟悉状态空间中的盲目搜索策略;(3)掌握盲目搜索算法,重点是宽度优先搜索和深度优先搜索算法。
2、实验要求用VC语言编程,采用宽度优先搜索和深度优先搜索方法,求解8数码问题3、实验内容(1)采用宽度优先算法,运行程序,要求输入初始状态假设给定如下初始状态S02 8 31 6 47 0 5和目标状态Sg2 1 64 0 87 5 3验证程序的输出结果,写出心得体会。
(2)对代码进行修改(选作),实现深度优先搜索求解该问题提示:每次选扩展节点时,从数组的最后一个生成的节点开始找,找一个没有被扩展的节点。
这样也需要对节点添加一个是否被扩展过的标志。
4 源代码及实验结果截图#include<stdio.h>#include<stdlib.h>#include<math.h>//八数码状态对应的节点结构体struct Node{int s[3][3];//保存八数码状态,0代表空格int f,g;//启发函数中的f和g值struct Node * next;struct Node *previous;//保存其父节点};int open_N=0; //记录Open列表中节点数目//八数码初始状态int inital_s[3][3]={2,8,3,1,6,4,7,0,5};//八数码目标状态int final_s[3][3]={2,1,6,4,0,8,7,5,3};//------------------------------------------------------------------------//添加节点函数入口,方法:通过插入排序向指定表添加//------------------------------------------------------------------------void Add_Node( struct Node *head, struct Node *p){struct Node *q;if(head->next)//考虑链表为空{ q = head->next;if(p->f < head->next->f){//考虑插入的节点值比链表的第一个节点值小p->next = head->next;head->next = p;}else {while(q->next)//考虑插入节点x,形如a<= x <=b{if((q->f < p->f ||q->f == p->f) && (q->next->f > p->f || q->next->f == p->f)){ p->next = q->next;q->next = p;break;}q = q->next;}if(q->next == NULL) //考虑插入的节点值比链表最后一个元素的值更大q->next = p;}else head->next = p;}//------------------------------------------------------------------------//删除节点函数入口//------------------------------------------------------------------------void del_Node(struct Node * head, struct Node *p ){struct Node *q;q = head;while(q->next){if(q->next == p){q->next = p->next;p->next = NULL;if(q->next == NULL) return;// free(p);}q = q->next;}}//------------------------------------------------------------------------//判断两个数组是否相等函数入口//------------------------------------------------------------------------int equal(int s1[3][3], int s2[3][3])int i,j,flag=0;for(i=0; i< 3 ; i++)for(j=0; j< 3 ;j++)if(s1[i][j] != s2[i][j]){flag = 1; break;}if(!flag)return 1;else return 0;}//------------------------------------------------------------------------//判断后继节点是否存在于Open或Closed表中函数入口//------------------------------------------------------------------------int exit_Node(struct Node * head,int s[3][3], struct Node *Old_Node) {struct Node *q=head->next;int flag = 0;while(q)if(equal(q->s,s)) {flag=1;Old_Node->next = q;return 1;}else q = q->next;if(!flag) return 0;}//------------------------------------------------------------------------//计算p(n)的函数入口//其中p(n)为放错位的数码与其正确的位置之间距离之和//具体方法:放错位的数码与其正确的位置对应下标差的绝对值之和//------------------------------------------------------------------------int wrong_sum(int s[3][3]){int i,j,fi,fj,sum=0;for(i=0 ; i<3; i++)for(j=0; j<3; j++){for(fi=0; fi<3; fi++)for(fj=0; fj<3; fj++)if((final_s[fi][fj] == s[i][j])){sum += fabs(i - fi) + fabs(j - fj);break;}}return sum;}//------------------------------------------------------------------------//获取后继结点函数入口//检查空格每种移动的合法性,如果合法则移动空格得到后继结点//------------------------------------------------------------------------int get_successor(struct Node * BESTNODE, int direction, struct Node *Successor)//扩展BESTNODE,产生其后继结点SUCCESSOR {int i,j,i_0,j_0,temp;for(i=0; i<3; i++)for(j=0; j<3; j++)Successor->s[i][j] = BESTNODE->s[i][j];//获取空格所在位置for(i=0; i<3; i++)for(j=0; j<3; j++)if(BESTNODE->s[i][j] == 0){i_0 = i; j_0 = j;break;} switch(direction){case 0: if((i_0-1)>-1 ){temp = Successor->s[i_0][j_0];Successor->s[i_0][j_0] = Successor->s[i_0-1][j_0];Successor->s[i_0-1][j_0] = temp;return 1;}else return 0;case 1: if((j_0-1)>-1){temp = Successor->s[i_0][j_0];Successor->s[i_0][j_0] = Successor->s[i_0][j_0-1];Successor->s[i_0][j_0-1] = temp;return 1;}else return 0;case 2: if( (j_0+1)<3){temp = Successor->s[i_0][j_0];Successor->s[i_0][j_0] = Successor->s[i_0][j_0+1];Successor->s[i_0][j_0+1] = temp;return 1;}else return 0;case 3: if((i_0+1)<3 ){temp = Successor->s[i_0][j_0];Successor->s[i_0][j_0] = Successor->s[i_0+1][j_0];Successor->s[i_0+1][j_0] = temp;return 1;}else return 0;}}//------------------------------------------------------------------------//从OPen表获取最佳节点函数入口//------------------------------------------------------------------------struct Node * get_BESTNODE(struct Node *Open){return Open->next;}//------------------------------------------------------------------------//输出最佳路径函数入口//------------------------------------------------------------------------void print_Path(struct Node * head){struct Node *q, *q1,*p;int i,j,count=1;p = (struct Node *)malloc(sizeof(struct Node));//通过头插法变更节点输出次序p->previous = NULL;q = head;while(q){q1 = q->previous;q->previous = p->previous;p->previous = q;q = q1;}q = p->previous;while(q){if(q == p->previous)printf("八数码的初始状态:\n");else if(q->previous == NULL)printf("八数码的目标状态:\n");else printf("八数码的中间态%d\n",count++);for(i=0; i<3; i++)for(j=0; j<3; j++){printf("%4d",q->s[i][j]);if(j == 2)printf("\n");}printf("f=%d, g=%d\n\n",q->f,q->g);q = q->previous;}}//------------------------------------------------------------------------//A*子算法入口:处理后继结点//------------------------------------------------------------------------void sub_A_algorithm(struct Node * Open, struct Node * BESTNODE, struct Node * Closed,struct Node *Successor){struct Node * Old_Node = (struct Node *)malloc(sizeof(struct Node));Successor->previous = BESTNODE;//建立从successor返回BESTNODE的指针Successor->g = BESTNODE->g + 1;//计算后继结点的g值//检查后继结点是否已存在于Open和Closed表中,如果存在:该节点记为old_Node,比较后继结点的g值和表中old_Node节点//g值,前者小代表新的路径比老路径更好,将Old_Node的父节点改为BESTNODE,并修改其f,g值,后者小则什么也不做。
C语言实现8数码问题

1、实验目的(1)熟悉人工智能系统中的问题求解过程;(2)熟悉状态空间中的盲目搜索策略;(3)掌握盲目搜索算法,重点是宽度优先搜索和深度优先搜索算法。
2、实验要求用VC语言编程,采用宽度优先搜索和深度优先搜索方法,求解8数码问题3、实验内容(1)采用宽度优先算法,运行程序,要求输入初始状态假设给定如下初始状态S02 8 31 6 47 0 5和目标状态Sg2 1 64 0 87 5 3验证程序的输出结果,写出心得体会。
(2)对代码进行修改(选作),实现深度优先搜索求解该问题提示:每次选扩展节点时,从数组的最后一个生成的节点开始找,找一个没有被扩展的节点。
这样也需要对节点添加一个是否被扩展过的标志。
4 源代码及实验结果截图#include<stdio.h>#include<stdlib.h>#include<math.h>//八数码状态对应的节点结构体struct Node{int s[3][3];//保存八数码状态,0代表空格int f,g;//启发函数中的f和g值struct Node * next;struct Node *previous;//保存其父节点};int open_N=0; //记录Open列表中节点数目//八数码初始状态int inital_s[3][3]={2,8,3,1,6,4,7,0,5};//八数码目标状态int final_s[3][3]={2,1,6,4,0,8,7,5,3};//------------------------------------------------------------------------ //添加节点函数入口,方法:通过插入排序向指定表添加//------------------------------------------------------------------------ void Add_Node( struct Node *head, struct Node *p){struct Node *q;if(head->next)//考虑链表为空{ q = head->next;if(p->f < head->next->f){//考虑插入的节点值比链表的第一个节点值小p->next = head->next;head->next = p;}else {while(q->next)//考虑插入节点x,形如a<= x <=b{if((q->f < p->f ||q->f == p->f) && (q->next->f > p->f || q->next->f == p->f)){p->next = q->next;q->next = p;break;}q = q->next;}if(q->next == NULL) //考虑插入的节点值比链表最后一个元素的值更大q->next = p;}}else head->next = p;}//------------------------------------------------------------------------//删除节点函数入口//------------------------------------------------------------------------void del_Node(struct Node * head, struct Node *p ){struct Node *q;q = head;while(q->next){if(q->next == p){q->next = p->next;p->next = NULL;if(q->next == NULL) return;// free(p);}q = q->next;}}//------------------------------------------------------------------------ //判断两个数组是否相等函数入口//------------------------------------------------------------------------ int equal(int s1[3][3], int s2[3][3]){int i,j,flag=0;for(i=0; i< 3 ; i++)for(j=0; j< 3 ;j++)if(s1[i][j] != s2[i][j]){flag = 1; break;}if(!flag)return 1;else return 0;}//------------------------------------------------------------------------ //判断后继节点是否存在于Open或Closed表中函数入口//------------------------------------------------------------------------ int exit_Node(struct Node * head,int s[3][3], struct Node *Old_Node){struct Node *q=head->next;int flag = 0;while(q)if(equal(q->s,s)) {flag=1;Old_Node->next = q;return 1;}else q = q->next;if(!flag) return 0;}//------------------------------------------------------------------------ //计算p(n)的函数入口//其中p(n)为放错位的数码与其正确的位置之间距离之和//具体方法:放错位的数码与其正确的位置对应下标差的绝对值之和//------------------------------------------------------------------------ int wrong_sum(int s[3][3]){int i,j,fi,fj,sum=0;for(i=0 ; i<3; i++)for(j=0; j<3; j++){for(fi=0; fi<3; fi++)for(fj=0; fj<3; fj++)if((final_s[fi][fj] == s[i][j])){sum += fabs(i - fi) + fabs(j - fj);break;}}return sum;}//------------------------------------------------------------------------//获取后继结点函数入口//检查空格每种移动的合法性,如果合法则移动空格得到后继结点//------------------------------------------------------------------------int get_successor(struct Node * BESTNODE, int direction, struct Node *Successor)//扩展BESTNODE,产生其后继结点SUCCESSOR{int i,j,i_0,j_0,temp;for(i=0; i<3; i++)for(j=0; j<3; j++)Successor->s[i][j] = BESTNODE->s[i][j];//获取空格所在位置for(i=0; i<3; i++)for(j=0; j<3; j++)if(BESTNODE->s[i][j] == 0){i_0 = i; j_0 = j;break;}switch(direction){case 0: if((i_0-1)>-1 ){temp = Successor->s[i_0][j_0];Successor->s[i_0][j_0] = Successor->s[i_0-1][j_0];Successor->s[i_0-1][j_0] = temp;return 1;}else return 0;case 1: if((j_0-1)>-1){temp = Successor->s[i_0][j_0];Successor->s[i_0][j_0] = Successor->s[i_0][j_0-1];Successor->s[i_0][j_0-1] = temp;return 1;}else return 0;case 2: if( (j_0+1)<3){temp = Successor->s[i_0][j_0];Successor->s[i_0][j_0] = Successor->s[i_0][j_0+1];Successor->s[i_0][j_0+1] = temp;return 1;}else return 0;case 3: if((i_0+1)<3 ){temp = Successor->s[i_0][j_0];Successor->s[i_0][j_0] = Successor->s[i_0+1][j_0];Successor->s[i_0+1][j_0] = temp;return 1;}else return 0;}}//------------------------------------------------------------------------ //从OPen表获取最佳节点函数入口//------------------------------------------------------------------------ struct Node * get_BESTNODE(struct Node *Open){return Open->next;}//------------------------------------------------------------------------ //输出最佳路径函数入口//------------------------------------------------------------------------ void print_Path(struct Node * head){struct Node *q, *q1,*p;int i,j,count=1;p = (struct Node *)malloc(sizeof(struct Node));//通过头插法变更节点输出次序p->previous = NULL;q = head;while(q){q1 = q->previous;q->previous = p->previous;p->previous = q;q = q1;}q = p->previous;while(q){if(q == p->previous)printf("八数码的初始状态:\n");else if(q->previous == NULL)printf("八数码的目标状态:\n");else printf("八数码的中间态%d\n",count++);for(i=0; i<3; i++)for(j=0; j<3; j++){printf("%4d",q->s[i][j]);if(j == 2)printf("\n");}printf("f=%d, g=%d\n\n",q->f,q->g);q = q->previous;}}//------------------------------------------------------------------------//A*子算法入口:处理后继结点//------------------------------------------------------------------------void sub_A_algorithm(struct Node * Open, struct Node * BESTNODE, struct Node * Closed,struct Node *Successor) {struct Node * Old_Node = (struct Node *)malloc(sizeof(struct Node));Successor->previous = BESTNODE;//建立从successor返回BESTNODE的指针Successor->g = BESTNODE->g + 1;//计算后继结点的g值//检查后继结点是否已存在于Open和Closed表中,如果存在:该节点记为old_Node,比较后继结点的g值和表中old_Node 节点//g值,前者小代表新的路径比老路径更好,将Old_Node的父节点改为BESTNODE,并修改其f,g值,后者小则什么也不做。
八数码问题启发函数和代价函数

八数码问题启发函数和代价函数八数码问题作为经典的搜索问题,其解决过程中启发函数和代价函数的选择对搜索效率有着重要的影响。
本文将针对八数码问题中启发函数和代价函数的选择进行探讨,并分析它们在搜索过程中的作用和影响。
一、启发函数的选择启发函数是在搜索过程中用来评估节点的“接近程度”的函数,它可以指导搜索算法朝着离目标更近的方向前进,从而提高搜索效率。
在八数码问题中,常用的启发函数有误放置数目、曼哈顿距离和线性冲突等。
1. 误放置数目误放置数目是指当前状态与目标状态中不同数字的个数,它可以作为启发函数来评估当前状态与目标状态的“距离”。
当误放置数目越小,说明当前状态距离目标状态越近,因此误放置数目可以作为一种简单而有效的启发函数。
2. 曼哈顿距离曼哈顿距离是指当前状态的每个数字到目标状态的正确位置之间的曼哈顿距离之和。
曼哈顿距离可以更准确地评估当前状态与目标状态的“距离”,因此在某些情况下,比误放置数目更适合作为启发函数。
3. 线性冲突线性冲突是指在某一行或某一列中有两个数字的目的位置相互交叉,这种情况下移动其中一个数字就会导致另一个数字也需要移动。
线性冲突可以影响搜索的效率,因此考虑线性冲突可以使启发函数更精确地评估当前状态与目标状态的“距离”。
二、代价函数的选择代价函数是指在搜索过程中用来评估节点的“代价”的函数,它可以指导搜索算法在选择候选节点时进行排序,从而提高搜索效率。
在八数码问题中,常用的代价函数有实际代价和估计代价等。
1. 实际代价实际代价是指从初始状态到当前状态的实际代价,它可以作为代价函数来评估当前状态的“代价”。
通过记录从初始状态到当前状态的实际代价,搜索算法可以更准确地评估每个候选节点的“代价”,从而更有针对性地选择下一个节点。
2. 估计代价估计代价是指从当前状态到目标状态的估计代价,它可以作为代价函数来评估当前状态的“代价”。
估计代价通常是通过启发函数来估计的,因此选择合适的启发函数对于估计代价的准确性非常重要。
八数码宽度优先搜索

/*程序利用C++程序设计语言,在VC6.0下采用宽度优先的搜索方式,成功的解决了八数码问题。
程序中把OPEN表和CLOSED表用队列的方式存储,大大地提高了效率,开始的时候要输入目标状态和起始状态,由于在宽度优先搜索的情况下,搜索过程中所走过的状态是不确定且很庞大的,所以程序最后输出宽度优先情况下最少步数的搜索过程以及程序运行所需要的时间*/#include "iostream"#include "stdio.h"#include "stdlib.h"#include "time.h"#include "string.h"#include <queue>#include <stack>using namespace std;constint N = 3;//3*3图enum Direction{None,Up,Down,Left,Right};//方向staticint n=0;staticint c=0;struct Map//图{int cell[N][N];//数码数组Direction BelockDirec;//所屏蔽方向struct Map * Parent;//父节点};//打印图voidPrintMap(struct Map *map){cout<<"*************************************************"<<endl;for(int i=0;i<N;i++){for(int j=0;j<N;j++){cout<<map->cell[i][j]<<" ";}cout<<endl;}cout<<"*************************************************"<<endl;}//移动图struct Map * MoveMap(struct Map * map,DirectionDirect,boolCreateNewMap){struct Map * NewMap;//获取空闲格位置inti,j;for(i = 0; i < N; i++){ boolHasGetBlankCell = false; for(j = 0; j < N; j++){if(map->cell[i][j] == 0){ HasGetBlankCell = true; break;}}if(HasGetBlankCell) break;}//移动数字intt_i = i,t_j = j; boolAbleMove = true; switch(Direct){case Down:t_i++;if(t_i>= N)AbleMove=false;break;case Up:t_i--;if(t_i< 0)AbleMove=false;break;case Left:t_j--;if(t_j< 0)AbleMove=false;break;case Right:t_j++;if(t_j>= N)AbleMove=false;break;};if(!AbleMove)//不可以移动则返回原节点{return map;}if(CreateNewMap){NewMap = new Map();for(int x = 0; x < N; x++)for(int y = 0; y < N; y++)NewMap->cell[x][y] = map->cell[x][y];}elseNewMap = map;NewMap->cell[i][j] = NewMap->cell[t_i][t_j];NewMap->cell[t_i][t_j] = 0;returnNewMap;}boolIsSuccess(struct Map * map,struct Map * Target){boolIsSuc = true;for(int i = 0; i < N; i++){for(int j = 0; j < N; j++){if(map->cell[i][j] != Target->cell[i][j]){IsSuc = false;break;}}if(!IsSuc)break;}returnIsSuc;}struct Map * BNF_Search(struct Map * begin,struct Map * Target) {struct Map * p1, *p2, *p=NULL;boolIsSucc = false;queue<struct Map *> Queue;if(IsSuccess(begin,Target))return begin;Queue.push(begin);do{p1 = Queue.front();Queue.pop();for (int i = 1; i <= 4; i++){Direction Direct=(Direction)i;if(Direct == p1->BelockDirec)//跳过屏蔽方向continue;p2 = MoveMap(p1,Direct,true);if(p2 != p1) //数码是否可以移动{p2->Parent = p1;switch(Direct)//设置屏蔽方向,防止往回推{case Up:p2->BelockDirec = Down;break;case Down:p2->BelockDirec = Up;break;case Left:p2->BelockDirec = Right;break;case Right:p2->BelockDirec = Left;break;}if (IsSuccess(p2,Target)){p = p2;return p;}Queue.push(p2);n++;}}}while(!Queue.empty() || p == NULL);return p;}intJou(struct Map *map) //将八数码转换成一个数列,并计算其逆序数{int a=0;char b[9];for(int i=0;i<N;i++){for(int j=0;j<N;j++)b[i*3+j]=map->cell[i][j];}for(int k=0;k<9;k++){for(int h=0;h<k;h++){if((b[h]<b[k])&&b[h]!=0)a++;}}return a%2;}int main(){int a1,a2;inti,j,m,n;int target[9];int flag;Map Target;Map *begin,*T;begin=new Map;cout<<"请输入八数码的目标状态(用0代替空格):"<<endl;//输入目标状态for (i=0;i<9;i++) //此for循环用来把输入的数存入到target数组中{flag=0;cin>>target[i];for(j=0;j<i;j++)if(target[i]==target[j])flag=1;if (target[i]<0||target[i]>8||flag==1) //判断输入是否正确{i--;cout<<"输入错误,请关闭重新运行!\n";}}int k=0;for (m=0;m<3;m++) //把数组target中的数传给图Target {for (n=0;n<3;n++){Target.cell[m][n]=target[k];k++;}}//输入起始状态cout<<"请输入八数码的起始状态(用0代替空格):"<<endl;for (i=0;i<9;i++){flag=0;cin>>target[i];for(j=0;j<i;j++)if(target[i]==target[j]) //判断输入的数是否正确flag=1;if (target[i]<0||target[i]>8||flag==1){i--;cout<<"输入错误,请关闭重新运行!\n";}}k=0;for (m=0;m<3;m++){for (n=0;n<3;n++){begin->cell[m][n]=target[k];k++;}}begin->Parent = NULL;begin->BelockDirec = None;cout<<"目标图:"<<endl;PrintMap(&Target);cout<<"起始图:"<<endl;PrintMap(begin);a1=Jou(&Target);a2=Jou(begin);if(a1!=a2){cout<<"无解"<<endl;exit(0); //无解的话就退出,重新运行}else{double start=clock();cout<<"有解"<<endl;//图搜索T=BNF_Search(begin,&Target);//打印if(T != NULL){//把路径倒序Map *p=T;stack<Map *> Stack1;while(p->Parent != NULL){Stack1.push(p);p = p->Parent;}cout<<"宽度优先最少步数的搜索过程为:"<<endl;while(!Stack1.empty()){PrintMap(Stack1.top());c++;Stack1.pop();}cout<<"\n完成!"<<endl;cout<<"找到目标状态所需要的最少步数为:"<<c<<endl;double end=clock();cout<<"程序运行的时间为:"<<end-start<<"ms"<<endl;}}return 0;}。
人工智能实验总结

总结
宽度优先搜索法
在有解的情形总能保证搜索到最短路经,也 就是移动最少步数的路径。但宽度优先搜索法的 最大问题在于搜索的结点数量太多,因为在宽度 优先搜索法中,每一个可能扩展出的结点都是搜 索的对象。随着结点在搜索树上的深度增大,搜 索的结点数会很快增长,并以指数形式扩张,从 而所需的存储空间和搜索花费的时间也会成倍增 长。
1 2
0 1 0 1
0 0 1 1
0 1 1 0
神经网络设计
用两层神经网络来实现,其中隐层为随机 感知器层(net1),神经网络元数目设计为 3,其权值和阈值是随机的,它的输出作为 输出层(分类层)的输入;输出层为感知 器层(net2),其神经元数为1,这里仅对 该层进行训练。
程序运行结果
随机感知器层的权值向量 iw1 = 0.4267 -0.6556 -0.5439 0.9376 -0.1007 -0.2886 随机感知器层的阈值向量 b1 = 0.4074 0.0441 0.8658
运行结果分析
上面实验结果可以看出,城市数目为30的 时候,当迭代次数为100,算法收敛慢,在 迭代次数内最优解没有达到稳定,没有搜 索到最好的解。 迭代次数为200和250的时候,算法基本达 到收敛,最优解在100代以后趋于稳定,表 明搜索到问题的最优解。
运行结果
当城市数目改变的时候: CityNum=50;最大代数gnmax=100;
程序运行结果
第二层感知器层的权值向量和阈值向量 iw2 = -3 -2 2 b2 = 2
八数码实验报告

八数码实验报告八数码实验报告引言:八数码,也被称为滑块拼图,是一种经典的益智游戏。
在这个实验中,我们将探索八数码问题的解决方案,并分析其算法的效率和复杂性。
通过这个实验,我们可以深入了解搜索算法在解决问题中的应用,并且探讨不同算法之间的优劣势。
1. 问题描述:八数码问题是一个在3x3的方格上进行的拼图游戏。
方格中有8个方块,分别标有1到8的数字,还有一个空方块。
游戏的目标是通过移动方块,将它们按照从左上角到右下角的顺序排列。
2. 算法一:深度优先搜索(DFS)深度优先搜索是一种经典的搜索算法,它从初始状态开始,不断地向前搜索,直到找到目标状态或者无法继续搜索为止。
在八数码问题中,深度优先搜索会尝试所有可能的移动方式,直到找到解决方案。
然而,深度优先搜索在解决八数码问题时存在一些问题。
由于搜索的深度可能非常大,算法可能会陷入无限循环,或者需要很长时间才能找到解决方案。
因此,在实际应用中,深度优先搜索并不是最优的选择。
3. 算法二:广度优先搜索(BFS)广度优先搜索是另一种常用的搜索算法,它从初始状态开始,逐层地向前搜索,直到找到目标状态。
在八数码问题中,广度优先搜索会先尝试所有可能的一步移动,然后再尝试两步移动,依此类推,直到找到解决方案。
与深度优先搜索相比,广度优先搜索可以保证找到最短路径的解决方案。
然而,广度优先搜索的时间复杂度较高,尤其是在搜索空间较大时。
因此,在实际应用中,广度优先搜索可能不太适合解决八数码问题。
4. 算法三:A*算法A*算法是一种启发式搜索算法,它在搜索过程中利用了问题的启发信息,以提高搜索效率。
在八数码问题中,A*算法会根据每个状态与目标状态之间的差异,选择最有可能的移动方式。
A*算法通过综合考虑每个状态的实际代价和启发式估计值,来评估搜索路径的优劣。
通过选择最优的路径,A*算法可以在较短的时间内找到解决方案。
然而,A*算法的实现较为复杂,需要合适的启发函数和数据结构。
八数码实验报告范文
八数码实验报告范文利用人工智能技术解决八数码游戏问题1.八数码游戏问题简介九宫排字问题(又称八数码问题)是人工智能当中有名的难题之一。
问题是在3某3方格盘上,放有八个数码,剩下第九个为空,每一空格其上下左右的数码可移至空格。
问题给定初始位置和目标位置,要求通过一系列的数码移动,将初始位置转化为目标位置。
2.八数码游戏问题的状态空间法表示①建立一个只含有初始节点0的搜索图g,把0放入open表中②建立cloed表,且置为空表③判断open表是否为空表,若为空,则问题无解,退出④选择open表中的第一个节点,把它从open表移出,并放入cloed表中,将此节点记为节点n⑤考察节点n是否为目标节点,若是,则问题有解,成功退出。
问题的解就是沿着n到0的路径得到。
若不是转⑥⑥扩展节点n生成一组不是n的祖先的后继节点,并将它们记为集合m,将m中的这些节点作为n的后继节点加入图g中⑦对未在g中出现过的(open和cloed表中未出现过的)集合m中的节点,设置一个指向父节点n的指针,并把这些节点放入open表中;对于已在g中出现过的m中的节点,确定是否需要修改指向父节点的指针;对于已在g中出现过并已在cloed表中的m中的节点,确定是否需要修改通向他们后继节点的指针。
⑧按某一任意方式或某种策略重排open表中节点的顺序⑨转③3.八数码游戏问题的盲目搜索技术宽度优先搜索:1、定义如果搜索是以接近起始节点的程度依次扩展节点的,那么这种搜索就叫做宽度优先搜索(breadth-firtearch)。
2、特点这种搜索是逐层进行的;在对下一层的任一节点进行搜索之前,必须搜索完本层的所有节点。
3、宽度优先搜索算法(1)把起始节点放到open表中(如果该起始节点为一目标节点,则求得一个解答)。
(2)如果open是个空表,则没有解,失败退出;否则继续。
(3)把第一个节点(节点n)从open表移出,并把它放入cloed的扩展节点表中。
“八”数码问题的宽度优先搜索与深度优先搜索
“八”数码问题的宽度优先搜索与深度优先搜索我在观看视频和查看大学课本及网上搜索等资料才对“八”数码问题有了更进一步的了解和认识。
一、“八”数码问题的宽度优先搜索步骤如下:1、判断初始节点是否为目标节点,若初始节点是目标节点则搜索过程结束;若不是则转到第2步;2、由初始节点向第1层扩展,得到3个节点:2、3、4;得到一个节点即判断该节点是否为目标节点,若是则搜索过程结束;若2、3、4节点均不是目标节点则转到第3步;3、从第1层的第1个节点向第2层扩展,得到节点5;从第1层的第2个节点向第2层扩展,得到3个节点:6、7、8;从第1层的第3个节点向第2层扩展得到节点9;得到一个节点即判断该节点是否为目标节点,若是则搜索过程结束;若6、7、8、9节点均不是目标节点则转到第4步;4、按照上述方法对下一层的节点进行扩展,搜索目标节点;直至搜索到目标节点为止。
二、“八”数码问题的深度优先搜索步骤如下:1、设置深度界限,假设为5;2、判断初始节点是否为目标节点,若初始节点是目标节点则搜索过程结束;若不是则转到第2步;3、由初始节点向第1层扩展,得到节点2,判断节点2是否为目标节点;若是则搜索过程结束;若不是,则将节点2向第2层扩展,得到节点3;4、判断节点3是否为目标节点,若是则搜索过程结束;若不是则将节点3向第3层扩展,得到节点4;5、判断节点4是否为目标节点,若是则搜索过程结束;若不是则将节点4向第4层扩展,得到节点5;6、判断节点5是否为目标节点,若是则搜索过程结束;若不是则结束此轮搜索,返回到第2层,将节点3向第3层扩展得到节点6;7、判断节点6是否为目标节点,若是则搜索过程结束;若不是则将节点6向第4层扩展,得到节点7;8、判断节点7是否为目标节点,若是则结束搜索过程;若不是则将节点6向第4层扩展得到节点8;9、依次类推,知道得到目标节点为止。
三、上述两种搜索策略的比较在宽度优先搜索过程中,扩展到第26个节点时找到了目标节点;而在深度优先搜索过程中,扩展到第18个节点时得到了目标节点。
宽度优先算法求解八数码问题
宽度优先算法求解八数码问题介绍八数码问题是一种经典的数学问题,在计算机科学中常用于算法研究和图搜索算法的测试。
它的目标是将一个3×3的九宫格中的数字从初始状态通过交换移动到目标状态。
宽度优先算法是一种常用的图搜索算法,适用于求解八数码问题。
它通过广度优先搜索图中的所有节点,直到找到目标节点。
本文将详细介绍宽度优先算法在求解八数码问题中的应用,包括算法原理、示例演示和应用场景。
算法原理宽度优先算法是一种盲目搜索算法,它使用队列(FIFO)数据结构来实现搜索过程。
它从初始状态开始,将其加入队列中,并继续搜索与初始状态相邻的所有状态。
然后,将与初始状态相邻的状态加入队列,并依次搜索下去。
直到找到目标状态,或者搜索完所有可能的状态。
为了避免重复搜索相同的状态,我们需要使用一个哈希表来记录已经访问过的状态。
每次搜索时,我们首先检查当前状态是否已经访问过,如果已经访问过则跳过,否则将其加入队列中并标记为已访问。
宽度优先算法的时间复杂度为 O(b^d),其中 b 是分支因子,d 是目标状态的深度。
在八数码问题中,分支因子为 4,深度一般不会超过 30,因此宽度优先算法具有较高的效率。
算法步骤宽度优先算法的求解步骤如下:1.初始化队列和哈希表,并将初始状态加入队列和哈希表中。
2.当队列不为空时,执行以下步骤:2.1 弹出队列的第一个状态。
2.2 检查当前状态是否为目标状态,如果是则结束搜索。
2.3 遍历当前状态的所有相邻状态。
2.4 对于每个相邻状态,检查是否已经访问过,如果没有则将其加入队列和哈希表中,并标记为已访问。
3.如果队列为空且没有找到目标状态,则无解。
示例演示为了更好地理解宽度优先算法在求解八数码问题中的应用,我们通过一个实际的例子来演示算法的执行过程。
假设我们有一个初始状态如下的八数码问题:2 8 31 47 6 5我们的目标是将其移动到如下的目标状态:1 2 38 47 6 5下面是宽度优先算法在求解该问题时的执行步骤:1.将初始状态加入队列和哈希表中。
八数码
3
在众多的搜索算法中,在八数码问题上我选用的是宽度优先搜索和A*算法两种。
(1)宽度优先搜索
宽度优先搜索过程如下:
1)把起始节点放到OPEN表中。
2)如果OPEN是个空表,则没有解,失败退出;否则继续。
(d)比较新旧路径代价。如果g(SUC)<g(OLD),则重新确定OLD的父辈节点为BESTNODE,记下较小代价g(OLD),并修正f(OLD)值。
(e)若至OLD节点的代价较低或一样,则停止扩展节点。
(f)若SUCCSSOR不在OPEN表中,则看其是否在CLOSED表中。
(g)若SUCCSSOR在CLOSED表中,则转向c。
③若在a中没有了该结点,则计算此结点的g、h、f和路径(与宽度优先搜索相同),并把此结点加入到a中,并转到步骤①中。
(e)输出结果(与宽度优先搜索相同)。
5
本次将课本的理论知识变成现实是一大进步,前段时期,程序运行的有问题,经过不懈的努力,终于还是成功了,总结经验,主要没有注意细节的把握,要编写一个程序,会产生错误的概率是很高的,而能够完全按照自己的要求实现某些功能的概率是比较小的,所以,在编程的过程中,要考虑周全,更不能有想当然的思想。
1)把起始节点放到OPEN表中。
2)判断初始结点是否是目标结点,若是则成功,退出;否则继续。
3)如果OPEN是个空表,则没有解,失败退出;否则继续。
4)把第一个节点(节点n)从OPEN表移出,并把它放入CLOSED扩展节点表中。
5)扩展节点n,将其子节点放入OPEN表的尾部,并为每个子节点配置指向节点n的指针
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
Y八数码问题具体思路:宽度优先算法实现过程(1)把起始节点放到OPEN表中;(2)如果OPEN是个空表,则没有解,失败退出;否则继续;(3)把第一个节点从OPEN表中移除,并把它放入CLOSED的扩展节点表中;(4)扩展节点n。
如果没有后继节点,则转向(2)(5)把n的所有后继结点放到OPEN表末端,并提供从这些后继结点回到n的指针;(6)如果n的任意一个后继结点是目标节点,则找到一个解答,成功退出,否则转向(2)。
深度优先实现过程(1)把起始节点S放入未扩展节点OPEN表中。
如果此节点为一目标节点,则得到一个解;(2)如果OPEN为一空表,则失败退出;(3)把第一个节点从OPEN表移到CLOSED表;(4)如果节点n的深度等于最大深度,则转向(2);(5)扩展节点n,产生其全部后裔,并把它们放入OPEN表的前头。
如果没有后裔,则转向(2);(6)如果后继结点中有任一个目标节点,则得到一个解,成功退出,否则转向(2)。
方法一:用C语言实现#include <stdio.h>#include <string.h>#include<stdlib.h>typedef long UINT64;typedef struct{char x; //位置x和位置y上的数字换位char y; //其中x是0所在的位置} EP_MOVE;#define SIZE 3 //8数码问题,理论上本程序也可解决15数码问题,#define NUM SIZE * SIZE //但move_gen需要做很多修改,输入初始和结束状态的部分和check_input也要修改#define MAX_NODE 1000000#define MAX_DEP 100#define XCHG(a, b) { a=a + b; b=a - b; a=a - b; }#define TRANS(a, b)/*{ long iii; (b)=0; for(iii=0; iii < NUM; iii++) (b)=((b) << 4) + a[iii]; }*/ //将数组a转换为一个64位的整数b#define RTRANS(a, b) \{ \long iii; \UINT64 ttt=(a); \for(iii=NUM - 1; iii >= 0; iii--) \{ \b[iii]=ttt & 0xf; \ttt>>=4; \} \} //将一个64位整数a转换为数组b//typedef struct EP_NODE_Tag{ UINT64 v; //保存状态,每个数字占4个二进制位,可解决16数码问题struct EP_NODE_Tag *prev; //父节点struct EP_NODE_Tag *small, *big;} EP_NODE;EP_NODE m_ar[MAX_NODE];EP_NODE *m_root;long m_depth; //搜索深度EP_NODE m_out[MAX_DEP]; //输出路径//long move_gen(EP_NODE *node, EP_MOVE *move){long pz; //0的位置UINT64 t=0xf;for(pz=NUM - 1; pz >= 0; pz--){if((node->v & t) == 0){ break; //找到0的位置}t<<=4;}switch(pz) {case 0: move[0].x=0; move[0].y=1; move[1].x=0; move[1].y=3; return 2; case 1: move[0].x=1; move[0].y=0; move[1].x=1; move[1].y=2; move[2].x=1; move[2].y=4; return 3; case 2: move[0].x=2; move[0].y=1; move[1].x=2; move[1].y=5;return 2; case 3: move[0].x=3; move[0].y=0; move[1].x=3; move[1].y=6; move[2].x=3; move[2].y=4; return 3; case 4: move[0].x=4; move[0].y=1; move[1].x=4; move[1].y=3; move[2].x=4; move[2].y=5; move[3].x=4; move[3].y=7; return 4; case 5: move[0].x=5; move[0].y=2; move[1].x=5;move[1].y=4; move[2].x=5; move[2].y=8; return 3; case 6: move[0].x=6; move[0].y=3; move[1].x=6; move[1].y=7; return 2; case 7: move[0].x=7; move[0].y=6; move[1].x=7; move[1].y=4; move[2].x=7; move[2].y=8; return 3; case 8: move[0].x=8; move[0].y=5; move[1].x=8; move[1].y=7;return 2;}return 0;}long mov(EP_NODE *n1, EP_MOVE *mv, EP_NODE *n2) //走一步,返回走一步后的结果{char ss[NUM];RTRANS(n1->v, ss);XCHG(ss[mv->x], ss[mv->y]);TRANS(ss, n2->v);return 0;}long add_node(EP_NODE *node, long r){EP_NODE *p=m_root;EP_NODE *q;while(p){ q=p;if(p->v == node->v) return 0;else if(node->v > p->v) p=p->big;else if(node->v < p->v) p=p->small;}m_ar[r].v=node->v;m_ar[r].prev=node->prev;m_ar[r].small=NULL;m_ar[r].big=NULL;if(node->v > q->v){ q->big= &m_ar[r];}else if(node->v < q->v){ q->small= &m_ar[r];}return 1;}/*得到节点所在深度*/long get_node_depth(EP_NODE *node){ long d=0;while(node->prev){ d++;node=node->prev;}return d;}/*返回值:成功-返回搜索节点数,节点数不够-(-1),无解-(-2)*/ long bfs_search(char *begin, char *end){ long h=0, r=1, c, i, j;EP_NODE l_end, node, *pnode;EP_MOVE mv[4]; //每个局面最多4种走法TRANS(begin, m_ar[0].v);TRANS(end, l_end.v);m_ar[0].prev=NULL;m_root=m_ar;m_root->small=NULL;m_root->big=NULL;while((h < r) && (r < MAX_NODE - 4)) { c=move_gen(&m_ar[h], mv);for(i=0; i < c; i++){ mov(&m_ar[h], &mv[i], &node); node.prev= &m_ar[h];if(node.v == l_end.v){ pnode= &node;j=0;while(pnode->prev){ m_out[j]=*pnode;j++;pnode=pnode->prev;}m_depth=j;return r;}if(add_node(&node, r)) r++; //只能对历史节点中没有的新节点搜索,否则会出现环}h++;printf("\rSearch...%9d/%d @ %d", h, r, get_node_depth(&m_ar[h]));}if(h == r){ return -2; }else{return -1; }}long check_input(char *s, char a, long r){ long i;for(i=0; i < r; i++){ if(s[i] == a - 0x30) return 0; }return 1;}long check_possible(char *begin, char *end){ char fs;long f1=0, f2=0;long i, j;for(i=0; i < NUM; i++){ fs=0;for(j=0; j < i; j++){if((begin[i] != 0) && (begin[j] != 0) && (begin[j] < begin[i])) fs++; }f1+=fs;fs=0;for(j=0; j < i; j++){ if((end[i] != 0) && (end[j] != 0) && (end[j] < end[i])) fs++;}f2+=fs;}if((f1 & 1) == (f2 & 1)) return 1;elsereturn 0;}void output(void){ long i, j, k;char ss[NUM];for(i=m_depth - 1; i >= 0; i--){ RTRANS(m_out[i].v, ss);for(j=0; j < SIZE; j++){ for(k=0; k < SIZE; k++){ printf("%2d", ss[SIZE * j + k]);}printf("\n");}printf("\n");}}int main(void){ char s1[NUM];char s2[NUM];long r;char a;printf("请输入开始状态:");r=0;while(r < NUM){ a=getchar();if(a >= 0x30 && a < 0x39 && check_input(s1, a, r)) { s1[r++]=a - 0x30;printf("%c", a);}}printf("\n请输入结束状态:");r=0;while(r < NUM){ a=getchar();if(a >= 0x30 && a < 0x39 && check_input(s2, a, r)) { s2[r++]=a - 0x30;printf("%c", a);}}printf("\n");if(check_possible(s1, s2)){ r=bfs_search(s1, s2);printf("\n");if(r >= 0){ printf("查找深度=%d,所有的方式=%ld\n", m_depth, r); output();}else if(r == -1){ printf("没有找到路径.\n");}else if(r == -2){printf("这种状态变换没有路径到达.\n");}else{printf("不确定的错误.\n");}}else{ printf("不允许这样移动!\n");}return 0;}方法二:用MATLAB实现program 8no_bfs; {八数码的宽度优先搜索算法} ConstDir : array[1..4,1..2]of integer {四种移动方向,对应产生式规则} = ((1,0),(-1,0),(0,1),(0,-1));n=10000;TypeT8no = array[1..3,1..3]of integer;TList = recordFather : integer; {父指针}dep : byte; {深度}X0,Y0 : byte; {0的位置}State : T8no; {棋盘状态 }end;VarSource,Target : T8no;List : array[0..10000] of TList; {综合数据库 }Closed,open,Best : integer { Best表示最优移动次数} Answer : integer; {记录解}Found : Boolean; {解标志}procedure GetInfo; {读入初始和目标节点}var i,j : integer;beginfor i:=1 to 3 dofor j:=1 to 3 do read(Source[i,j]);for i:=1 to 3 dofor j:=1 to 3 do read(Target[i,j]);end;procedure Initialize; {初始化}var x,y : integer;beginFound:=false;Closed:=0;open:=1;with List[1] do beginState:=Source;dep:=0;Father:=0;For x:=1 to 3 doFor y:=1 to 3 doif State[x,y]=0 then Begin x0:=x;y0:=y; End;end;end;Function Same(A,B : T8no):Boolean; {判断A,B状态是否相等 }Var i,j : integer;BeginSame:=false;For i:=1 to 3 do for j:=1 to 3 do if A[i,j]<>B[i,j] then exit;Same:=true;End;Function not_Appear(new : tList):boolean;{判断new是否在List中出现 }var i : integer;beginnot_Appear:=false;for i:=1 to open do if Same(new.State,List[i].State) then exit;not_Appear:=true;end;procedure Move(n : tList;d : integer;var ok : boolean;var new : tList);{将第d条规则作用于n得到new,OK是new是否可行的标志 }var x,y : integer;beginX := n.x0 + Dir[d,1];Y := n.y0 + Dir[d,2];{判断new的可行性}if not ((X > 0) and ( X < 4 ) and ( Y > 0 ) and ( Y < 4 )) then begin ok:=false;exitend;OK:=true;new.State:=n.State; {new=Expand(n,d)}new.State[X,Y]:=0;new.State[n.x0,n.y0]:=n.State[X,Y];new.X0:=X;new.Y0:=Y;end;procedure Add(new : tList); {插入节点new}beginif not_Appear(new) then Begin {如果new没有在List出现 } Inc(open); {new加入open表 }List[open] := new;end;end;procedure Expand(Index : integer; var n : tList); {扩展n的子节点} var i : integer;new : tList;OK : boolean;Beginif Same(n.State , Target) then begin {如果找到解}Found := true;Best :=n.Dep;Answer:=Index;end;For i := 1 to 4 do begin {依次使用4条规则} Move(n,i,OK,new);if not ok then continue;new.Father := Index;new.Dep :=n.dep + 1;Add(new);end;end;procedure GetOutInfo; {输出}procedure Outlook(Index : integer); {递归输出每一个解} var i,j : integer;beginif Index=0 then exit;Outlook(List[Index].Father);with List[Index] dofor i:=1 to 3 do beginfor j:=1 to 3 do write(State[i,j],' ');writeln;end;writeln;beginWriteln('Total = ',Best);Outlook(Answer);end;procedure Main; {搜索主过程} beginRepeatInc(Closed);Expand(Closed,List[Closed]); {扩展Closed} Until (Closed>=open) or Found;if Found then GetOutInfo {存在解}else Writeln('no answer'); {无解}end;BeginAssign(Input,'input.txt');ReSet(Input);Assign(Output,'Output.txt');ReWrite(Output);GetInfo;Initialize;Main;Close(Input);Close(Output);End.五、实验结果六、实验总结通过实验问题的求解过程就是搜索的过程,采用适合的搜索算法是关键的,因为对求解过程的效率有很大的影响,包括各种规则、过程和算法等推理技术。