Heuristic search:
启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标。这样可以省略大量无谓的搜索路径,提到了效率。在启发式搜索中,对位置的估价是十分重要的。采用了不同的估价可以有不同的效果。
启发中的估价是用估价函数表示的,如:f(n) = g(n) + h(n)
其中f(n) 是节点n的估价函数,g(n)是在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目标节点最佳路径的估计代价。在这里主要是h(n)体现了搜索的启发信息,因为g(n)是已知的。如果说详细点,g(n)代表了搜索的广度的优先趋势。但是当h(n) >>g(n)时,可以省略g(n),而提高效率。
对于启发式搜索,比较由浅入深的文章可以见:
http://blog.csdn.net/v_july_v/article/details/6093380
本文主要是使用启发式搜索实际解决八数码难题。
参考文章:http://blog.renren.com/share/244406827/3440309244
问题描述:
在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字。棋盘中留有一个空格,空格用0来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。
例如:283104765 输出是:4
由八数码问题的部分状态图可以看出,从初始节点开始,在通向目标节点的路径上,各节点的数码格局同目标节点相比较,其数码不同的位置个数在逐渐减少,最后为零。所以,这个数码不同的位置个数便是标志一个节点到目标节点距离远近的一个启发性信息,利用这个信息就可以指导搜索。即可以利用启发信息来扩展节点的选择,减少搜索范围,提高搜索速度。
启发函数设定。对于八数码问题,可以利用棋局差距作为一个度量。搜索过程中,差距会逐渐减少,最终为零,为零即搜索完成,得到目标棋局。
(三)数据结构与算法设计
该搜索为一个搜索树。为了简化问题,搜索树节点设计如下:
struct Chess//棋盘
{
int cell[N][N];//数码数组
int Value;//评估值
Direction BelockDirec;//所屏蔽方向
struct Chess * Parent;//父节点
};
intcell[N][N]; 数码数组:记录棋局数码摆放状态。
int Value; 评估值:记录与目标棋局差距的度量值。
DirectionBelockDirec; 所屏蔽方向:一个屏蔽方向,防止回推。
Direction :enumDirection{None,Up,Down,Left,Right};//方向枚举
struct Chess *Parent; 父节点:指向父亲节点。
下一步可以通过启发搜索算法构造搜索树。
1、局部搜索树样例:
2、搜索过程
搜索采用广度搜索方式,利用待处理队列辅助,逐层搜索(跳过劣质节点)。搜索过程如下:
(1)、把原棋盘压入队列;
(2)、从棋盘取出一个节点;
(3)、判断棋盘估价值,为零则表示搜索完成,退出搜索;
(4)、扩展子节点,即从上下左右四个方向移动棋盘,生成相应子棋盘;
(5)、对子节点作评估,是否为优越节点(子节点估价值小于或等于父节点则为优越节点),是则把子棋盘压入队列,否则抛弃;
(5)、跳到步骤(2);
#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=30;//最大搜索深度
enum Direction{None,Up,Down,Left,Right};//方向
struct Chess//棋盘 {
int cell[N][N];//数码数组
int Value;//评估值
Direction BelockDirec;//所屏蔽方向
struct Chess * Parent;//父节点
};
//移动棋盘
struct Chess * MoveChess(struct Chess * TheChess,Direction Direct,bool CreateNewChess) {
struct Chess * NewChess;
//获取空闲格位置
int i,j;
for(i=0;i<N;i++) {
bool HasGetBlankCell=false;
for(j=0;j<N;j++) {
if(TheChess->cell[i][j]==0) {
HasGetBlankCell=true;
break;
}
}
if(HasGetBlankCell)
break;
}
//移动棋盘
struct Chess * MoveChess(struct Chess * TheChess,Direction Direct,bool CreateNewChess) {
struct Chess * NewChess;
//获取空闲格位置
int i,j;
for(i=0;i<N;i++) {
bool HasGetBlankCell=false;
for(j=0;j<N;j++) {
if(TheChess->cell[i][j]==0) {
HasGetBlankCell=true;
break;
}
}
if(HasGetBlankCell)
break;
}
//移动数字
int t_i=i,t_j=j;
bool AbleMove=true;
switch(Direct) //空白格周围的表格移动
{ case Up:
t_i++;
if(t_i>=N)
AbleMove=false;
break;
case Down:
t_i--;
if(t_i<0)
AbleMove=false;
break;
case Left:
t_j++;
if(t_j>=N)
AbleMove=false;
break;
case Right:
t_j--;
if(t_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->cell[x][y]=TheChess->cell[x][y];
}
} else
NewChess=TheChess;
NewChess->cell[i][j]=NewChess->cell[t_i][t_j]; NewChess->cell[t_i][t_j]=0;
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->cell[i][j]!=Target->cell[i][j])
Value++;
}
}
TheChess->Value=Value;
return Value;
}
//搜索函数
struct Chess * Search(struct Chess* Begin,struct Chess * Target) {
Chess * p1,*p2,*p;
int Step=0;//深度
p=NULL;
queue<struct Chess *> Queue1;
Queue1.push(Begin);
//搜索
do{
p1 = (struct Chess *)Queue1.front();
Queue1.pop();
for(int i=1;i<=4;i++)//分别从四个方向推导出新子节点 {
Direction Direct= (Direction)i;
if(Direct==p1->BelockDirec)//跳过屏蔽方向
continue;
p2=MoveChess(p1,Direct,true);//移动数码
if(p2!=p1)//数码是否可以移动 {
Appraisal(p2,Target);//对新节点估价
if(p2->Value<=p1->Value)//是否为优越节点
{
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;
}
Queue1.push(p2);//存储节点到待处理队列
if(p2->Value==0)//为0则,搜索完成 {
p=p2;
i=5;
}
} else {
delete p2;//为劣质节点则抛弃
p2=NULL;
}
}
}