隐式图的搜索问题
实验内容:
编写九宫重排问题的启发式搜索(A*算法)求解程序。
在3х3组成的九宫棋盘上,放置数码为1~8的8个棋子,棋盘中留有一个空格,空格周围的棋子可以移动到空格中,从而改变棋盘的布局。根据给定初始布局和目标布局,编程给出一个最优的走法序列。输出每个状态的棋盘
测试数据:初始状态:123456780 目标状态:012345678
主要功能
启发式搜索算法的描述:
(1)把初始节点S0 放入Open表中,f(S0)=g(S0)+h(S0);
(2)如果Open表为空,则问题无解,失败退出;
(3)把Open表的第一个节点取出放入Closed表,并记该节点为n;
(4)考察节点n是否为目标节点。若是,则找到了问题的解,成功退出;
(5)若节点n不可扩展,则转到第(2)步;
(6)扩展节点n,生成子节点ni(i=1,2,……),计算每一个子节点的估价值f(ni) (i=1,2,……),并为每一个子节点设置指向父节点的指针,然后将这些子节点放入Open表中;
(7)根据各节点的估价函数值,对Open表中的全部节点按从小到大的顺序重新进行排序;
(8)转到第(2)步。
实验代码
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
struct node
{
int nine[3][3];//数码状态
int f;//估价值
int direct;//空格移动方向
node *parent;//父节点
node(int direct) :direct(direct){}
bool operator ==(node &a)
{
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
if(a.nine[i][j]!=this->nine[i][j])
return false;
return true;
}
node& operator =(node& a)
{
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
this->nine[i][j]=a.nine[i][j];
return *this;
}
};
int pos[9]={4,0,1,2,5,8,7,6,3};
//每一位是某个数字应该在的位置所代表的数,除3和对3取余可以求出行列
void print(node* p)
{
for(int i=0;i<3;i++)
{
for(int j=0;j<3;j++)
cout<<" "<<p->nine[i][j]<<' ';
cout<<endl;
}
cout<<endl;
}
vector<node*> open; //open表
vector<node*> close; //close表
vector<node*>::iterator up(vector<node*>& v,node* p)
{
vector<node*>::iterator i=v.begin();
for(;i<v.end();i++)
if((*(*i))==*p)
break;
return i;
}
int cntH(node* p)
{
int h=0;
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
if(p->nine[i][j]!=0)
h+=abs(pos[p->nine[i][j]]/3-i)+abs(pos[p->nine[i][j]]%3-j);
return h;
}
int f(node* p)
{
return cntH(p)+p->direct;
}
bool cmp(node* p,node* q)
{
return f(p)>f(q);
}
int find0(node& p)
{
for(int i=0;i<3;i++)
for(int j=0;j<3;j++)
if(p.nine[i][j]==0)
return i*3+j;
}
node* search(node& s)
{
int drc=0;
open.push_back(&s);
while(!open.empty())
{
sort(open.begin(),open.end(),cmp);
node* p=open.back();
open.pop_back();
if(!cntH(p))
return p;
drc=p->direct+1;
int zerop=find0(*p),x=zerop/3,y=zerop%3;
for(int i=0;i<4;i++)
{
int xs=0,ys=0;
if(i==0) xs=0,ys=1;
else if(i==1) xs=0,ys=-1;
else if(i==2) xs=1,ys=0;
else if(i==3) xs=-1,ys=0;
if(x+xs<0||x+xs>=3||y+ys<0||y+ys>=3)
continue;
node* q=new node(drc);
q->parent=p;
*q=*p;
q->nine[x][y]=q->nine[x+xs][y+ys];
q->nine[x+xs][y+ys]=0;
bool skip=0;
vector<node*>::iterator dup=up(open,q);
if(dup!=open.end())
{
if(f(q)<f(*dup))
{
(*dup)->direct=q->direct;
(*dup)->parent=q->parent;
}
skip=1;
}
dup=up(close,q);
if(dup!=close.end())
if(f(q)<f(*dup))
{
close.erase(dup);
delete* dup;
open.push_back(q);
skip=1;
}
if(!skip)
open.push_back(q);
}
close.push_back(p);
}
}
void prints(node* p)
{
vector<node*> trace;
while(p)
{
trace.push_back(p);
p=p->parent;
}
int coun=0;
while(!trace.empty())
{
cout<<"----------step"<<coun<<"------------"<<endl;
print(trace.back());
cout<<"f:"<<f(trace.back())<<" g:"<<coun<<" h:"<<cntH(trace.back())<<endl<<endl;
trace.pop_back();
coun++;
}
}
int main()
{
node p(0);
p.nine[0][0]=2;
p.nine[0][1]=8;
p.nine[0][2]=3;
p.nine[1][0]=1;
p.nine[1][1]=6;
p.nine[1][2]=4;
p.nine[2][0]=7;
p.nine[2][1]=0;
p.nine[2][2]=5;
p.parent=nullptr;
node* q;
q=search(p);
prints(q);
return 0;
}
运行截图
总结
中间有个==写成了=,找了半天bug气死我了,一开始我没有具体实现思路,还是先看了好久别人的代码才大概有思路,写了大概4个小时,调试又花了很多时间,这个实验让我对于A星算法了解程度大大提升,上次人工智能实验老师给了代码,我就偷懒看了一遍,没有自己打一遍,这次算是补上了。