八数码问题

http://blog.sina.com.cn/s/blog_a01c59f101015tjd.html

八数码问题

《八数码问题》这是课程中的一个实验,问题比较有趣,所有代码都是本人自行设计编写,所以特意写出来供大家学习和参考,做到这个实验的同学千万不要完整复制我写的代码哦,参考学习一下,你可以自己写出更好的代码。


 

问题描述:

给定九宫格的初始状态,要求在有限步的操作内,使其转化为目标状态,且所得到的解是代价最小解(即移动的步数最少)例如:

2   3           1 2 3

1 8 4     ->    8   4

7 6 5           7 6 5


 

求解思路:

用启发式搜索算法求解,A*算法

首先定义了六个结构体:一是表示九宫格的各个数字,二是表示OPEN结点,三是表示OPEN表,四是表示CLOSED结点,五是表示CLOSED表,六是表示求解路径。自定义的函数有:初始化九宫格判断两个九宫格是否一致求某结点的hx,即与目标结点九宫格不一样的元素个数OPEN表按照估价函数值的大小从小到大排序判断某九宫格是否与OPEN表中某结点相同判断某九宫格是否与CLOSED表中某结点相同将一个九宫格中的数字全部复制到另一个九宫格中OPEN表中某结点的扩展结点删除OPEN表中第一个结点计算估价函数值并赋值以矩阵形式打印九宫格打印求解最终路径启发式搜索函数寻找求解路径


 

算法描述:

procedure heuristic_search

     open =[start]  closed=[ f(s) = g(s) + h(s) 

  while  open != [ ] do

    begin

    open 表中删除第一个状态,称为n ;

  if n = 目的状态  then return (success) 

  生成的所有子状态;

  if n 没有任何子状态 then continue ;

  for  n  的每个子状态 do 

         case  子状态  is  not  already  on  open  or  closed 表 :

       begin 

              计算该子状态的估价函数值;

       将该子状态加到open 表中;

       end 

    case  子状态 is  already  on  open  表 :

       if 该子状态是沿着一条比在open 表已有的更短路径而到达

       then 记录更短路径走向及其估价函数值;

    case  子状态 is  already  on  closed 表:

       if 该子状态是沿着一条比在closed 表已有的更短路径而到达

       begin 

           将该子状态从closed 表移到 open 表中;

           记录更短路径走向及其估价函数值;

       end 

    case  end 

    将放入closed 表中;

    根据估价函数值,从小到大重新排列open 表;

  end 

  return failure);

End


 

求解代码:

#include<iostream.h>

#include<malloc.h>

 

#define MAXOPEN         300

#define MAXCLOSE        600

#define MAXPATH         300


 

 

//自定义结构体

typedef struct{

int num[3][3];       //九宫格数字

}Squared;                //九宫格


 

typedef struct{

Squared stateself;   //九宫格

int dx;              //结点在搜索树中的深度

int hx;              //结点对应九宫格与目标九宫格不一样的元素个数

int fx;              //估价函数fx=dx+hx

}Open;                   //OPEN结点


 

typedef struct{

Open squanote[MAXOPEN];   //记录未扩展结点

Open squapare[MAXOPEN];   //未扩展结点对应的父结点

}OpenSqua;               //OPEN表记录未扩展结点


 

typedef struct{

int num;             //CLOSED结点代价值

Squared stateself;   //九宫格

}Closed;                 //CLOSED结点


 

typedef struct{

Closed squanote[MAXCLOSE]; //记录已扩展结点

}ClosedSqua;             //CLOSED表记录已扩展结点


 

typedef struct{

Open pa[MAXPATH];         //保存求解路径

}Path;                   //求解路径

//自定义结构体


 

 

//初始化九宫格

void InitSqua(Squared &s){

for(int i=0;i<3;i++){

for(int j=0;j<3;j++){

s.num[i][j]=0;    //3*3矩阵各数字初始化为0

}

}

}

//初始化九宫格


 

 

//判断两个九宫格是否一致

bool EqualSqua(Squared a,Squared b){

for(int i=0;i<3;i++){

for(int j=0;j<3;j++){

if(a.num[i][j]!=b.num[i][j])

return 0;     //如果有不同的元素就返回0

}

}

return 1;                 //一致就返回1

}

//判断两个九宫格是否一致


 

 

//求某结点的hx,即与目标结点九宫格不一样的元素个数

int GetHx(Squared a,Squared end){

int count=0;

for(int i=0;i<3;i++){

for(int j=0;j<3;j++){

if(a.num[i][j]!=end.num[i][j])

count++;

}

}

return count;

}

//求某结点的hx,即与目标结点九宫格不一样的元素个数


 

 

//OPEN表按照估价函数值的大小从小到大排序

void SortOpen(OpenSqua &op,int n){

int i,j;

for(i=0;i<n-1;i++){

int index=i;

for(j=i+1;j<n;j++){

if(op.squanote[j].fx<op.squanote[index].fx)

index=j;

if(index!=i){

Open temp=op.squanote[i];

op.squanote[i]=op.squanote[index];

op.squanote[index]=temp;

}

}

}

}

//OPEN表按照估价函数值的大小从小到大排序


 

 

//判断某九宫格是否与OPEN表中某结点相同

bool ExistOpen(OpenSqua op,Squared s){

for(int i=0;i<MAXOPEN;i++){

if(EqualSqua(op.squanote[i].stateself,s))

return 1;

}

return 0;

}

//判断某九宫格是否与OPEN表中某结点相同


 

 

//判断某九宫格是否与CLOSED表中某结点相同

bool ExistClosed(ClosedSqua close,Squared s){

for(int i=0;i<MAXCLOSE;i++){

if(EqualSqua(close.squanote[i].stateself,s))

return 1;

}

return 0;

}

//判断某九宫格是否与CLOSED表中某结点相同


 

 

//将一个九宫格中的数字全部复制到另一个九宫格中

void CopySqua(Squared &a,Squared b){

for(int i=0;i<3;i++){

for(int j=0;j<3;j++){

a.num[i][j]=b.num[i][j];

}

}

}

//将一个九宫格中的数字全部复制到另一个九宫格中


 

 

//OPEN表中某结点的扩展结点

bool CreateSub(Open s[],Open note){

int i,j,k=0;

for(i=0;i<3;i++){

for(j=0;j<3;j++){

if(note.stateself.num[i][j]==0){  //寻找九宫格空缺所在的坐标

if(i-1>=0){                   //向上移动

CopySqua(s[k].stateself,note.stateself);

s[k].stateself.num[i][j]=note.stateself.num[i-1][j];

s[k].stateself.num[i-1][j]=note.stateself.num[i][j];

s[k].dx=1;

k++;

}

if(j+1<=2){                   //向右移动

CopySqua(s[k].stateself,note.stateself);

s[k].stateself.num[i][j]=s[k].stateself.num[i][j+1];

s[k].stateself.num[i][j+1]=0;

s[k].dx=1;

k++;

}

if(i+1<=2){                   //向下移动

CopySqua(s[k].stateself,note.stateself);

s[k].stateself.num[i][j]=s[k].stateself.num[i+1][j];

s[k].stateself.num[i+1][j]=0;

s[k].dx=1;

k++;

}

if(j-1>=0){                   //向左移动

CopySqua(s[k].stateself,note.stateself);

s[k].stateself.num[i][j]=s[k].stateself.num[i][j-1];

s[k].stateself.num[i][j-1]=0;

s[k].dx=1;

k++;

}

return 1;

}

}

}

return 0;

}

//OPEN表中某结点的扩展结点


 

 

//删除OPEN表中第一个结点

void DeleteOne(OpenSqua &a,int cnt){

int i;

for(i=0;i<cnt;i++){

a.squanote[i]=a.squanote[i+1];

}

}

//删除OPEN表中第一个结点


 

 

//计算估价函数值并赋值

void CalEvaluate(Open &op,Open pare,Squared end){

op.dx=pare.dx+1;                 //dx

op.hx=GetHx(op.stateself,end);   //hx

op.fx=op.dx+op.hx;               //fx

}

//计算估价函数值并赋值


 

 

//以矩阵形式打印九宫格

void PrintSqua(Squared s){

for(int i=0;i<3;i++){

cout<<“\t”;

for(int j=0;j<3;j++){

if(s.num[i][j]==0)

cout<<”  “;

else

cout<<s.num[i][j]<<” “;

}

cout<<“\n”;

}

}

//以矩阵形式打印九宫格


 

 

//打印求解最终路径

void ShowPath(Path path,int cnt){

int i,j,smp=cnt-1,count=0;

Open *endPath;

endPath=new Open[cnt];

for(i=smp;i>0;i–){

i=smp;

for(j=i-1;j>=0;j–){

if(GetHx(path.pa[j].stateself,path.pa[i].stateself)==2){

endPath[count++]=path.pa[i];

break;

}

}

smp=j;

}

endPath[count++]=path.pa[0];

for(i=count-1;i>=0;i–){

PrintSqua(endPath[i].stateself);

cout<<“\n”;

}

}

//打印求解最终路径


 

 

//启发式搜索函数寻找求解路径

bool Heuristic_Search(Squared start,Squared end){

int cntop=0,cntcl=0,cntpa=0;

int count=0;

OpenSqua open;                         //OPEN

ClosedSqua close;                      //CLOSED

Path path;                             //求解路径

Open nop;                              //标记为n的结点

open.squanote[cntop].stateself=start;  //初始化OPEN

open.squanote[cntop].dx=0;             //初始化OPEN

open.squanote[cntop].hx=GetHx(open.squanote[cntop].stateself,end);        //初始化OPEN

open.squanote[cntop].fx=open.squanote[cntop].dx+open.squanote[cntop].hx;  //初始化OPEN

cntop++;

 

while(cntop!=0){                       //OPEN表不为空

nop=open.squanote[0];              //OPEN表中第一个结点给标记为n的结点

path.pa[cntpa++]=nop;                //记录求解路径

DeleteOne(open,cntop);             //OPEN表中删除第一个状态

cntop–;                           

if(EqualSqua(nop.stateself,end)){

ShowPath(path,cntpa);          //如果找到了目标状态则输出求解路径

return 1;

}

Open sub[4];                       //扩展结点最多4

CreateSub(sub,nop);                //初始化n结点的扩展结点

if(!CreateSub(sub,nop))

continue;                      //如果没有扩展结点就跳出进行下一次循环

for(int i=0;i<4;i++){

if(sub[i].dx==1){              //对于n的每个存在的子状态

if(!ExistOpen(open,sub[i].stateself)&&!ExistClosed(close,sub[i].stateself)){

 

CalEvaluate(sub[i],nop,end);

open.squanote[cntop]=sub[i];

open.squapare[cntop]=nop;

cntop++;

continue;

}

if(ExistOpen(open,sub[i].stateself)){

 

CalEvaluate(sub[i],nop,end);

if(sub[i].fx<nop.fx){

open.squanote[cntop]=sub[i];

open.squapare[cntop]=nop;

cntop++;

continue;

}

}

if(ExistClosed(close,sub[i].stateself)){

 

CalEvaluate(sub[i],nop,end);

if(sub[i].fx<close.squanote[0].num){

open.squanote[cntop]=sub[i];

open.squapare[cntop]=nop;

cntop++;

continue;

}

}

}

}

close.squanote[cntcl].stateself=nop.stateself;     //n放入closed 表中;

close.squanote[cntcl].num=nop.fx;                  //n放入closed 表中;

cntcl++;                                           

SortOpen(open,cntop);                              //根据估价函数值,从小到大重新排列open 

if(count++>300){

break;

}

}

return 0;

}

//启发式搜索函数寻找求解路径


 

 

//在主函数中测试

void main(){

cout<<“========================九宫重排问题========================\n”;

cout<<“问题描述:\n\t给定九宫格的初始状态,要求在有限步的操作内,使其转化为\n”;

cout<<”    目标状态,且所得到的解是代价最小解(即移动的步数最少)\n”;

cout<<”        例如输入 1 2 3 4 0 5 6 7 8 \n”;

cout<<“则表示你所要表示的九宫格为:\n”;

cout<<“\t1 2 3\n\t4   5\n\t6 7 8\n”;

cout<<“========================输入始末状态========================\n”;

Squared inits,goals;     //初始与目标状态九宫格

int i,j,x;

InitSqua(inits);         //初始化初始状态九宫格

InitSqua(goals);         //初始化目标状态九宫格

cout<<“请输入九宫的初始状态:(数字为空时用0替代)\n”;

for(i=0;i<3;i++){

for(j=0;j<3;j++){

cin>>x;

inits.num[i][j]=x;

}

}

cout<<“您输入的初始状态为:\n”;

PrintSqua(inits);        ////打印初始状态九宫格

cout<<“请输入九宫的目标状态:(数字为空时用0替代)\n”;

for(i=0;i<3;i++){

for(j=0;j<3;j++){

cin>>x;

goals.num[i][j]=x;

}

}

cout<<“您输入的目标状态为:\n”;

PrintSqua(goals);        ////打印目标状态九宫格

cout<<“========================打印求解路径========================\n”;

cout<<“求解路径为:\n”;

if(Heuristic_Search(inits,goals)){

cout<<“success!\n”;

}else{

cout<<“no result!\n”;

}

cout<<“========================问题演示结束========================\n”;

}

//在主函数中测试

 

心得体会:

本题程序的编写基本上就是花了一个比较长的整时间进行的编写。刚开始没有一点头绪,想想每种策略都会有很多分支,不知如何取舍,在仔细阅读书本中有关A*算法的描述后,结合实验指导书提供的算法伪代码,基本上确立了整体的框架,在进入主题之前,我首先解决的是九宫格的输入输出问题,使得不仅方便输入,而且输出的九宫格也很形象。结合算法中所需要的操作,又编写了复制九宫格、计算代价、排序、判断某九宫格是否在某一表中、求扩展结点等辅助函数。最后在核心算法函数中进行调用,根据A*算法,编写出了搜索函数,在测试过程中,每次都从最简单的情况开始测试,寻找错误的地方,一步一步进行纠正,最终将书上的案例数据都测试通过了,不过本程序有一些缺陷,我使用了定长顺序表来存放选择步骤,所以这个对于简单的案例会造成占用了较多的空间,却没有用上,而对于复杂的案例,也许顺序表不够长,最终无法得出正确结果。考虑到本题没有判断某数据是否有解的函数,所以只能一步一步搜索,为防止无止境的搜索,我仍然没有改变定长顺序表,这样当搜索进行一定步骤之后,如果还是无解,我就认为这是无解的。最后,同样对界面进行了优化。

这个程序最大的缺陷就是,我也不知道是否能解所有问题,比较简单的测试数据可以得到正确解,而有的测试数据,也不知道是否真的无解。这个问题比较纠结了。个人感觉结构体设计的不太好,浪费空间资源,数据有些冗余,有待改进。

谢谢!《八数码问题》

    原文作者:九宫格问题
    原文地址: https://blog.csdn.net/dengm155/article/details/53079343
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞