八数码问题有许多种解决方法,深度搜索、宽度搜索、启发式......下面就启发式搜索来解一下八数码问题。
先来说一下基本的原理:
启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标。这样可以省略大量无谓的搜索路径,提高了效率。在启发式搜索中,对位置的估价是十分重要的。采用了不同的估价可以有不同的效果。 启发中的估价是用估价函数表示的,如: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),而提高效率。
算法如下:
#include <stdio.h>
#include <stdlib.h>
int total_step = 0;
int open_number;//共遍历了open_number个结点
int end[3][3] = {{1,2,3},
{8,0,4},
{7,6,5}};
typedef struct Eigntnumbers
{
int array[3][3];
int d; //深度
int w; //放错棋子数
int f; //f=d+n;估价函数
int ope; //方向
struct Eigntnumbers * next; //后
struct Eigntnumbers * up; //前
struct Eigntnumbers * parent;
}Eightnumbers;
Eightnumbers *open,*close;//open表 closed表
int judge(Eightnumbers *p,int end[3][3]); //判断放错棋子的位置
void open_insert(Eightnumbers *head,Eightnumbers *i)//插入到open
{
Eightnumbers *p1,*p2;
p1 = head->next;
p2 = head;
while(p1!=NULL && (i->f >= p1->f))//要插在比结点的w值小的地方
{
p2 = p1;
p1 = p1->next;
}
p2->next = i;
i->up = p2;
i->next = p1;
if(p1!=NULL) //插在p2 p1之间
{
p1->up = i;
}
//printf("f=%4d\n",i->f);
}
int init()//open表初始化
{
int k;
Eightnumbers *p;
open = (Eightnumbers *)malloc(sizeof(Eightnumbers)); //open表初始化
close = (Eightnumbers *)malloc(sizeof(Eightnumbers)); //close表初始化
open->up = open->next = close->up = close->next = NULL;
p=(Eightnumbers *)malloc(sizeof(Eightnumbers));
p->array[0][0] = 3;
p->array[0][1] = 1;
p->array[0][2] = 8;//2,0,3
p->array[1][0] = 7;
p->array[1][1] = 6;
p->array[1][2] = 4;//1,8,4
p->array[2][0] = 0;
p->array[2][1] = 2;
p->array[2][2] = 5;//7,6,5
//k = judge(p,end);
//p->w = k;
p->d = 0;
//p->f = p->w+p->d;
p->parent = NULL;
open_insert(open,p);//初始化,初始状态的结点插入到open
return 1;
}
int judge(Eightnumbers *p,int end[3][3]) //判断放错棋子的位置
{
int count=0;
int a,b;//count标记放错的棋子数;
if(p->array[0][0] != end[0][0])
count++;
if(p->array[0][1] != end[0][1])
count++;
if(p->array[0][2] != end[0][2])
count++;
if(p->array[1][0] != end[1][0])
count++;
if(p->array[1][1] != end[1][1])
count++;
if(p->array[1][2] != end[1][2])
count++;
if(p->array[2][0] != end[2][0])
count++;
if(p->array[2][1] != end[2][1])
count++;
if(p->array[2][2] != end[2][2])
count++;
return count;//除去空的位置
}
Eightnumbers *open_first(Eightnumbers *head) //取 open 表第一个结点,此节点在open表中删除,加入到close表
{
Eightnumbers *p;
//a = p->next;
if(head->next == NULL)
{
return NULL;
}
p = head->next;
head->next = p->next;
if(p->next!=NULL)
{
p->next->up = head;
}
p->up = NULL;
p->next = NULL;
return p;
}
void close_add(Eightnumbers *head,Eightnumbers *i)//结点i加入到closed表中//删除Open表中结点i
{
int a,b;
//Eightnumbers *a;
//结点i加入到closed表中
//a = i->next;
i->next = head->next;
i->up = head;
head->next = i;
if(i->next!=NULL)
{
i->next->up = i;
}
}
int judgenew_open(Eightnumbers *p1,Eightnumbers *open)//判断新扩展的结点在不在open表中
{
int m=0;
Eightnumbers *p;
p = open->next;
while(p != NULL)
{
//判断新扩展的结点在open表中,并且新节点s与p1节点一样
if(p->array[0][0] == p1->array[0][0] && p->array[0][1] == p1->array[0][1] && p->array[0][2] == p1->array[0][2] &&
p->array[1][0] == p1->array[1][0] && p->array[1][1] == p1->array[1][1] && p->array[1][2] == p1->array[1][2] &&
p->array[2][0] == p1->array[2][0] && p->array[2][1] == p1->array[2][1] && p->array[2][2] == p1->array[2][2])
{
m = 1;
break;
}
p = p->next;
}
return m;
}
int judgenew_close(Eightnumbers *p1,Eightnumbers *close)//判断新扩展的结点在不在open closed表中
{
int k=0;
Eightnumbers *j;
j = close->next;
while(j != NULL)
{
//判断新扩展的结点在closed表中,.并且新节点s与j1节点一样
if(j->array[0][0] == p1->array[0][0] && j->array[0][1] == p1->array[0][1] && j->array[0][2] == p1->array[0][2] &&
j->array[1][0] == p1->array[1][0] && j->array[1][1] == p1->array[1][1] && j->array[1][2] == p1->array[1][2] &&
j->array[2][0] == p1->array[2][0] && j->array[2][1] == p1->array[2][1] && j->array[2][2] == p1->array[2][2])
{
k = 1;
break;
}
j = j->next;
}
return k;
}
int show(Eightnumbers *item)
{
Eightnumbers *p;
int i;
int step;
p=item;
if(p!=NULL)
{
step=show(p->parent);
printf("\nStep %d:\n",step+1);
for(i=0;i<3;i++)
printf("%4d%4d%4d\n",p->array[i][0],p->array[i][1],p->array[i][2]);
printf("\n");
return step+1;
}
else
{
return -1;
}
}
void free_list(Eightnumbers *head)
{
Eightnumbers *p,*q;
p=head->next;
while ( p!=NULL )
{
q=p->next;
free(p);
p=q;
}
free(head);
}
int opera(int i1,int ii,int j1,Eightnumbers *p1)
{
int i=4;
int a1,b1;
//printf("i1=%4d,ii=%4d\n",i1,ii);
if(j1 == 0 && i1!=0)//上
{
a1 = p1->array[i1][ii]; //改变了数值、深度、放错棋子数、f
b1 = p1->array[i1-1][ii];
//printf("1交换前%4d%4d\n",p1->array[i1][ii],p1->array[i1-1][ii]);
p1->array[i1][ii] = b1; //改变了数值、深度、放错棋子数、f
p1->array[i1-1][ii] = a1;
//swap(p1->array[i1][ii],p1->array[i1-1][ii]);
p1->ope = 0;
}
if(j1 == 1 && ii!=0)//左
{
a1 = p1->array[i1][ii]; //改变了数值、深度、放错棋子数、f
b1 = p1->array[i1][ii-1];
//printf("2交换前%4d%4d\n",p2->array[i1][ii],p2->array[i1][ii-1]);
p1->array[i1][ii] = b1; //改变了数值、深度、放错棋子数、f
p1->array[i1][ii-1] = a1;
//printf("2交换后%4d%4d\n",p2->array[i1][ii],p2->array[i1][ii-1]);
}
if(j1 == 2 && i1!=2)//下
{
a1 = p1->array[i1][ii]; //改变了数值、深度、放错棋子数、f
b1 = p1->array[i1+1][ii];
// printf("3交换前%4d%4d\n",p3->array[i1][ii],p3->array[i1+1][ii]);
p1->array[i1][ii] = b1; //改变了数值、深度、放错棋子数、f
p1->array[i1+1][ii] = a1;
//printf("3交换后%4d%4d\n",p3->array[i1][ii],p3->array[i1+1][ii]);
;
}
if(j1 == 3 && ii!=2)//右
{
a1 = p1->array[i1][ii]; //改变了数值、深度、放错棋子数、f
b1 = p1->array[i1][ii+1];
//printf("4交换前%4d%4d\n",p4->array[i1][ii],p4->array[i1][ii+1]);
p1->array[i1][ii] = b1; //改变了数值、深度、放错棋子数、f
p1->array[i1][ii+1] = a1;
}
return 1;
}
int extend(Eightnumbers *p) //扩展结点i,生成其全部结点
{
Eightnumbers *p1;
int i1,ii,flag=0;
int j1,direction=4;
int a1,b1;
int cc,dd;
int w1;
int k1,k2;
int j,ji;
for(i1=0;i1<3;i1++) //查找空格的位置在哪里
{
for(ii=0;ii<3;ii++)
{
if(p->array[i1][ii] == 0)
{
flag=1;break;
}
}
if(flag == 1) //找到了空格位置,即为(i,ii)
break;
}
//printf("i1=%d,ii=%d\n",i1,ii);
//可以从4个方向移动,4个方向依次为:上、左、下、右
for(j1=0;j1<direction;j1++)
{
p1=(Eightnumbers *)malloc(sizeof(Eightnumbers));
p1->d = p->d;
p1->w = p->w;
p1->f = p->f;
for(j=0;j<3;j++)
for(ji=0;ji<3;ji++)
p1->array[j][ji] = p->array[j][ji];
opera(i1,ii,j1,p1);
k1 = judgenew_open(p1,open);
k2 = judgenew_close(p1,close);
//printf("k1=%4d k2=%4d\n\n",k1,k2);
//judge_open_close(p1,close,open,p);
if(k1==0&&k2==0)//调用judegnew函数,判断扩展的新结点在不在open closed表中)
{
p1->parent = p;
p1->d = p1->d+1;
w1 = judge(p1,end);
p1->w = w1;
p1->f = p1->d + w1;
//
//printf("f=%4d\n",p1->w);
if(p1->w == 0) //放错棋子数为0,目标状态达到
{
total_step=show(p1);
printf("共: %d\n",total_step);
free_list(open);
free_list(close);
return 1;//
}
else
{
open_number++;
open_insert(open,p1);
}
}
else
{
free(p1);
}
}
return 0;
}
int main()
{
Eightnumbers *p1;
int i=0,ii=0;
int a,b;
int first[3][3] = {{3,1,8},
{7,6,4},
{0,2,5}};
printf("******************八数码问题******************\n");
printf("*************八数码问题的初始状态*************\n");
for(i=0;i<3;i++)
{
for(ii=0;ii<3;ii++)
printf("%4d",first[i][ii]);
printf("\n");