实验内容
用A搜索算法实现重排九宫
实验原理
1).评价函数
以当前状态下各将牌到目标位置的距离之和作为节点的评价标准。距离的定义为:“某将牌行下标与目标位置行下标之差的绝对值 + 列下标与目标位置列下标之差的绝对值”。距离越小,该节点的效果越好。某个状态所有将牌到目标位置的距离之和用“h值”表示。
2).主要函数
(1).countH(state & st);
countH函数功能是计算st状态的h值。
计算过程中将会用到rightPos数组,数组里记录的是目标状态下,0~9每个将牌在九宫格里的位置(位置 = 行下标 * 3 + 列下标)。
(2).f(state * p);
f()=h()+level
(3).look_up_dup(vector<state*> & vec, state * p);
在open表或close表中,是否存在指定状态p,当找到与p完全相等的节点时,退出函数。
(4).search(state & start);
在open表不为空时,按f值由小到大对open表中元素进行排序。
调用findZero()函数找到0值元素的位置。空格可以向上下左右四个方向移动,前提是移动后不能越过九宫格的边界线。确定某方向可走后,空格移动一步,生成状态p’。
此时,检查open表中是否已有p’,若有,更新p’数据;检查close表中是否已有p’,若有,将p’从close表中删除,添加到open表中。
重复的执行这个过程,直到某状态的h值为零。
(5).dump_solution(state * q);
在终端输出解路径。
实验代码与截图
// A*算法
#include<iostream>
#include<vector>
#include<time.h>
#include<algorithm>
using namespace std;
const int GRID = 3; //Grid表示表格的行数(列数),这是3*3的九宫格
int rightPos[9] = { 4, 0, 1, 2, 5, 8, 7, 6, 3 };
//目标状态时,若p[i][j]=OMG,那么3*i+j = rightPos[OMG]
struct state{
int panel[GRID][GRID];
int level; //记录深度
int h;
state * parent;
state(int level) :level(level){}
bool operator == (state & q){
//判断两个状态是否完全相等(对应位置元素相等),完全相等返回true,否则返回false
for (int i = 0; i<GRID; i++){
for (int j = 0; j<GRID; j++){
if (panel[i][j] != q.panel[i][j])
return false;
}
}
return true;
}
state & operator = (state & p){ //以状态p为当前状态赋值,对应位置元素相同
for (int i = 0; i<GRID; i++){
for (int j = 0; j<GRID; j++){
panel[i][j] = p.panel[i][j];
}
}
return *this;
}
};
void dump_panel(state * p){ //将八数码按3*3矩阵形式输出
for (int i = 0; i<GRID; i++){
for (int j = 0; j<GRID; j++)
cout << p->panel[i][j] << " ";
cout << endl;
}
}
int countH(state & st){ //给定状态st,计算它的h值。
int h = 0;
for (int i = 0; i<GRID; i++){
for (int j = 0; j<GRID; j++){
if (st.panel[i][j] != 0)
h += abs(rightPos[st.panel[i][j]] / GRID - i) +
abs(rightPos[st.panel[i][j]] % GRID - j);
//h=各个将牌与其目标位置的距离之和.距离定义为:行下标之差的绝对值+列下标之差的绝对值。
}
}
return h;
}
int findZero(state & st){ //找到零值元素,返回其在表中的位置
for (int i = 0; i<GRID; i++){
for (int j = 0; j<GRID; j++){
if (st.panel[i][j] == 0)
return i * 3 + j;
}
}
}
int f(state * p){ //计算并返回f()值,即h值+level
return countH(*p) + p->level;
}
bool compare_state(state * p, state * q){ //比较两个状态的f值
return f(p) > f(q);
}
vector<state *> open_table; //open表
vector<state *> close_table; //close表
vector<state*>::iterator look_up_dup(vector<state*> & vec, state * p){
vector<state*>::iterator it_r = vec.begin();
for (; it_r<vec.end(); it_r++){
if ((*(*it_r)) == *p){
break;
}
}
return it_r;
}
state * search(state & start){ //A*算法进行搜索
int level = 0;
open_table.push_back(&start);
int count = 0;
while (!open_table.empty()){
sort(open_table.begin(), open_table.end(), compare_state);
//对open表中的元素进行排序
state * p = open_table.back();
open_table.pop_back();
if (countH(*p) == 0)
return p; //所有将牌到达目标位置,搜索过程结束
level = p->level + 1;
int zeroPos = findZero(*p);
int x = zeroPos / 3; //空格的行下标
int y = zeroPos % 3; //空格的列下标
for (int i = 0; i<4; i++){ //上下左右四个方向
int x_offset = 0, y_offset = 0;
switch (i){
case 0:x_offset = 0, y_offset = 1; break; //右
case 1:x_offset = 0, y_offset = -1; break;//左
case 2:x_offset = 1, y_offset = 0; break;//上
case 3:x_offset = -1, y_offset = 0; break;//下
};
if (x + x_offset<0 || x + x_offset >= GRID || y + y_offset<0 || y + y_offset >= GRID){
continue;
//若移动一步后,将超出上/下/左/右边界,则这个方向不可走,尝试下一个方向
}
state * q = new state(level); //这个方向可走,扩展下一个节点
q->parent = p;
*q = *p;
q->panel[x][y] = q->panel[x + x_offset][y + y_offset];
q->panel[x + x_offset][y + y_offset] = 0;//空格沿这个方向移一步
bool skip = false;
vector<state *>::iterator dup = look_up_dup(open_table, q);
//若q已在open表中,则对open表中的信息进行更新
if (dup != open_table.end()){
if (f(q) < f(*dup)){
(*dup)->level = q->level;
(*dup)->parent = q->parent;
}
skip = true;
}
dup = look_up_dup(close_table, q);
if (dup != close_table.end()){ //若q已在close表中,且f值比原值小,
if (f(q) < f(*dup)){ //则将q从close表清除,加入open表
delete *dup;
close_table.erase(dup);
open_table.push_back(q);
skip = true;
}
}
if (!skip){
open_table.push_back(q);
}
}
close_table.push_back(p);
}
}
void dump_solution(state * q) //输出解路径
{
vector<state *> trace;
while (q){
trace.push_back(q);
q = q->parent;
}
int count = 0;
while (!trace.empty()){
cout << "Step " << count <<endl;
dump_panel(trace.back());
cout << "h: " << countH(*trace.back()) <<"\tg:"<<count<< "\tf: " << f(trace.back()) << endl;
cout<<"------------------------------\n\n";
trace.pop_back();
count++;
}
}
int main()
{
state p(0);
state *q;
p.panel[0][0] = 2;//设置初始状态
p.panel[0][1] = 8;
p.panel[0][2] = 3;
p.panel[1][0] = 1;
p.panel[1][1] = 6;
p.panel[1][2] = 4;
p.panel[2][0] = 7;
p.panel[2][1] = 0;
p.panel[2][2] = 5;
p.parent = NULL;
q = search(p);
dump_solution(q);
system("pause");
}
总结
启发式搜索是在搜索中加入了与问题有关的启发性信息,用以指导搜索朝着最有希望的方向前进,加速问题的求解过程并找到最优解。A* 算法实际上就是一种启发式搜索,利用一个估价函数评估每次的的决策的价值,决定先尝试哪一种方案,这样可以极大的优化普通的广度优先搜索。在启发式搜索中,对位置的估价是十分重要的。采用了不同的估价可以有不同的效果。启发中的估价是用估价函数表示的,如:f(n) = g(n) + h(n)其中f(n)是节点n的估价函数,g(n)是在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目标节点最佳路径的估计代价。在此九宫问题中,显然g(n)就是从初始状态变换到当前状态所移动的步数,估计函数f(n)我们就可采用当前状态各个数字牌不在目标状态未知的个数,即错位数。
2017.4.28周五下午续——第二种方案
#define N 3
#include "stdlib.h"
#include "stdio.h"
static int B[N][N]={{1,2,3},{8,0,4},{7,6,5}};
typedef struct node{
int s[N][N];
int value;
int parent;
}state;
state open[100],close[100];
int f,r,f1,r1;
void change(int a[N][N],state b[],int l){
int i,j;
for(i=0;i<N;i++)
for(j=0;j<N;j++)
a[i][j]=b[l].s[i][j];
}
int com(int A[N][N],int C[N][N]){
int i,j,a;
a=0;
for(i=0;i<N;i++)
for(j=0;j<N;j++)
if(A[i][j]!=C[i][j]) a++;
return(a);
}
int depth(state A[],int i){
int a;
a=0;
while(i!=0){
i=A[i].parent;
a++;
}
return(a+1);
}
void sort(){
int i,j,k,l,l1,l2;
state A;
for(i=f;i<r-1;i++){
k=i;
for(j=i+1;j<r;j++){
if(open[j].value<open[k].value){
k=j;
}
}
for(l1=0;l1<N;l1++){
for(l2=0;l2<N;l2++){
A.s[l1][l2]=open[k].s[l1][l2];
open[k].s[l1][l2]=open[i].s[l1][l2];
open[i].s[l1][l2]=A.s[l1][l2];
}
}
A.value=open[k].value;
open[k].value=open[i].value;
open[i].value=A.value;
A.parent=open[k].parent;
open[k].parent=open[i].parent;
open[i].parent=A.parent;
for(l=f;l<r;l++){
if(open[l].parent==k){
open[l].parent=i;
}
}
}
}
void heuristic_search1(int A[N][N]){
int i,j,k,l,l1,l2,val;
i=j=0;
while(i<r){
if(com(A,open[i].s)!=0){
i++;
}
else break;
}
while(j<r1){
if(com(A,close[i].s)!=0){
j++;
}
else break;
}
if(i>=r&&j>=r1){
val=com(A,B)+depth(open,f-1)+1;
for(l1=0;l1<N;l1++)
for(l2=0;l2<N;l2++)
open[r].s[l1][l2]=A[l1][l2];
open[r].value=val;
open[r].parent=f-1;
r++;
}
else if(i<r){
if(depth(open,f-1)+1<depth(open,i))
{
r++;
for(l1=0;l1<N;l1++)
for(l2=0;l2<N;l2++){
open[i].s[l1][l2]=A[l1][l2];
open[i].value=val;
open[r].parent=f-1;
}
}
}
else {
if(depth(close,f1)+1<depth(close,j)){
r++;
for(l1=0;l1<N;l1++)
for(l2=0;l2<N;l2++)
open[r].s[l1][l2]=A[l1][l2];
open[r].value=val;
open[r].parent=f-1;
for(l=j;l<r1;l++)
{
for(l1=0;l1<N;l1++)
for(l2=0;l2<N;l2++){
close[l].s[l1][l2]=close[l+1].s[l1][l2];
close[l].value=close[l+1].value;
close[l].parent=close[l+1].parent;
r1--;
}
}
}
}
}
void heuristic_search(int search[N][N])
{
int i,j,k,A[N][N],h,b,c,d,e,val;
int l1,l2,road[20];
for(i=0;i<N;i++)
for(j=0;j<N;j++)
{
open[0].s[i][j]=search[i][j];
close[0].s[i][j]=0;
}
open[0].value=depth(open,0)+com(open[0].s,B);
open[0].parent=-1;
f=f1=r1=0; r=1;
while(f!=r)
{
e=f;
for(i=0;i<N;i++)
for(j=0;j<N;j++)
close[r1].s[i][j]=open[f].s[i][j];
close[r1].value=open[f].value;
close[r1].parent=open[f].parent;
r1++;
f++;
change(A,open,e);
if(com(A,B)==0)
{
printf("\n success!");
k=e;
l1=0;
while(k!=-1){
road[l1]=k;
l1++;
k=open[k].parent;
}
for(k=l1;k>=0;k--){
printf("\tStep %d\n",l1-k);
for(i=0;i<N;i++){
for(j=0;j<N;j++)
if(open[road[k]].s[i][j]!=0)
printf("\t%d",open[road[k]].s[i][j]);
else printf("\t ");
printf("\n");
}
}
return;
}
for(i=0;i<N;i++){
for(j=0;j<N;j++)
if(A[i][j]==0)
break;
if(j<N) break;
}
if(i+1<N)
{
b=A[i+1][j];
A[i+1][j]=A[i][j];
A[i][j]=b;
heuristic_search1(A);
}
change(A,open,e);
if(i-1>-1){
b=A[i-1][j];
A[i-1][j]=A[i][j];
A[i][j]=b;
heuristic_search1(A);
}
change(A,open,e);
if(j+1<N){
b=A[i][j+1];
A[i][j+1]=A[i][j];
A[i][j]=b;
heuristic_search1(A);
}
change(A,open,e);
if(j-1>-1){
b=A[i][j-1];
A[i][j-1]=A[i][j];
A[i][j]=b;
heuristic_search1(A);
}
change(A,open,e);
sort();
}
printf("\n failure");
}
int main()
{
int A[N][N],i,j;
printf("please input the original state:\n");
for(i=0;i<N;i++)
for(j=0;j<N;j++)
scanf("%d",&A[i][j]);
heuristic_search(A);
return 0;
}