八数码的问题
程序员文章站
2023-12-24 16:25:45
...
最近我研究了一下八数码问题,采用迭代深入A*搜索(IDA*)和递归深入优先搜索.实现八数码难题.IDA*即迭代加深的A*搜索,实现代码是最简练的,无须状态判重,无需估价排序。那么就用不到哈希表,堆上也不必应用,空间需求变的超级少。效率上,应用了曼哈顿距离。同时可以根据深度和h值,在找最优解的时候,对超过目前最优解的地方进行剪枝,这可以导致搜索深度的急剧减少,所以,这,是一个致命的剪枝!因此,IDA*大部分时候比A*还要快,可以说是A*的一个优化版本!IDA*算法跟典型的迭代深入算法的差别在于IDA*采用耗散值而不是树的深度来减支的.以下是这个程序的;递归最佳优先搜索是动态调整迭代截断值值的一种方法, 优势在于可以在搜索过程中更加灵
活得选择最优路径,算法伪代码参照<<人工智能一种现代化方法>>。
代码编译环境采用的是vs2012,移植到g++编译器,可能会不兼容,需要酌情修改。
启发函数:
启发函数采用曼哈顿距离。
例如在平面上,坐标(x1, y1)的i点与坐标(x2, y2)的j点的曼哈顿距离为:
d(i,j)=|X1-X2|+|Y1-Y2|.
代码见功能中的f和dist函数
int dist(int p1,int p2){
int dx=abs(p1%3-p2%3+0.0);
int dy=abs(p1/3-p2/3+0.0);
return dx+dy;
}
int f(int g,int* arr){
int i,s = 0,hn=0;
for(i=0;i<9;i++){
s=s*9+arr[i];
}
for(i=8;i>=0;i--)
{
switch(s%9)
{
case 0: hn+=dist(i,8); break;
case 1: hn+=dist(i,0); break;
case 2: hn+=dist(i,3); break;
case 3: hn+=dist(i,6); break;
case 4: hn+=dist(i,1); break;
case 5: hn+=dist(i,4); break;
case 6: hn+=dist(i,7); break;
case 7: hn+=dist(i,2); break;
case 8: hn+=dist(i,5); break;
default: break;
}
s/=9;
}
return g+hn;
}
IDA*的伪代码如下:
IDA*函数代码:
char* idas(int* s, int* g){
char* path = new char[200]; //保存路径
stack<Node *> stk;
Node * p;
p->deepth = 0;
limit = f(p->deepth,s);
stk.push(new Node(s));
while(limit<200){
next_limit = 200;
while(!stk.empty()){
p = stk.top();
stk.pop();
if(p->deepth!=0){
path[p->deepth-1] = p->action;
}
if(f(p->deepth,p->arr) > limit){
next_limit = ((f(p->deepth,p->arr) > next_limit)?next_limit:f(p->deepth,p->arr));
}
else{
if(true == p->isGoal()){
path[p->deepth] = '\0';
return path;
}
vector<Node*> v = p->getSons();
for(int i=0;i<v.size();i++){
v[i]->deepth = p->deepth+1;
stk.push(v[i]);
}
}
}
limit = next_limit;
}
return NULL;
}
递归最佳优先搜索是一个模仿标准的递归算法,不沿当前不确定路径继续下去,而是记录从当前节点的祖先可得到的最佳可替换路径的f值。如果当前节点超过了这个限制,递归将转回到替换路径上去。当递归回溯后,对回溯前的当前路径上的每个节点。RBFS用其子节点的最佳f值替换其f值。这样在下一次判断时方便我们确定是否再次扩展此值。
char* rbfs(int* s, int* g){
char * path = new char[100];
Node * node = new Node(s); //初始化起始节点
node->cost = f(0,s);
rbfs(node,1000,path); //递归递归优先搜索
return path;
}
Node* rbfs(Node *node,int limit,char * path){
if(node->isGoal(targetArr)){
path[node->deepth] = '\0';
return node;
}
vector<Node *> vt = node->getSons();
if(vt.empty()){
node->cost = MAXVALUE; //1000代表无穷大
return NULL;
}
for(int i=0;i<vt.size();i++){
vt[i]->cost = Max(f(vt[i]->deepth,vt[i]->arr),f(node->deepth,node->arr));
}
while(true){
//排序方便求最大最小
sort(vt.begin(),vt.end(),cmp);
Node* best = vt[0];
path[best->deepth-1] = best->action;
if(best->cost > limit){ //如果扩展的子节点们的最小值比之前的节点的次大值大,返回NULL,把当前的代价赋为扩展子节点的最小值,方便以后判断是否扩展该节点,这是一个重要的剪枝
node->cost = best->cost;
return NULL;
}
//Min((vt.size()==1 ? MAXVALUE:vt[1]->cost),limit)是求次大节点的耗散值与上层所有节点的次大值的最小者
Node * result = rbfs(best,Min((vt.size()==1 ? MAXVALUE:vt[1]->cost),limit),path);
if(NULL != result) return result; //找到目标 一层一层往上返回
}
}
转载于:https://blog.51cto.com/webcrawler/1193742