问题描述:八数码问题即是找出一条状态路径,使初始状态(start)转换到目标状态(end),一般选取目标状态为:1 2 3 4 5 6 7 8 0(0代表空格)

0   1   2               1   2   3

3   4   5  ——>   4  5   6

6   7   8               7  8   0

下面通过几个问题来对求解策略进行讨论。

(Q1)任意给定初态和终态,是否存在路径(可解性)?

八数码问题的可解性,存在一个判定定理:八数码问题可解当且仅当初态数字(除空格)的逆序数之和与终态的逆序数之和奇偶性一致。详细课参考http://wenku.baidu.com/link?url=PtvqjecMh5AJNaXso2Lq2Yg_VSpyDZpj8-3noewRp0CEs_-NcMZTn61Nb2pwxKkVf1R3o-wC7YKDgUkiUajFoHeni73_wd3OStpTOEKGQoS

(Q2)如何存储状态节点?

采用3*3或者1*9矩阵来存储状态节点,笔者也不例外,因为该存储结构方便计算评价函数值以及打印路径等。但是在此基础上,笔者添加了一个mark域(一个int型),sizeof(int)=4*8=8*4,即能表示8个十六进制数,则能够标志唯一一个状态,这样两个状态是否相同的判断就从9个int型数据的比较变为1个int型的比较,因此能够节省大量的时间(虽然说,计算标志时也相当于一次矩阵比较,但是却能做到“一劳永逸”)。

(Q3)为什么选择链式存储结构?

与静态存储结构相比,链式存储结构是“量体裁衣”,不会造成空间的浪费;另一方面,对于不同的初态,搜索的深度不知,静态表的长度也不能确定一个合适的值(既不浪费也能够用);最重要的一点,搜索过程,经常要进行节点的删除,添加,若用静态链表存储必定会浪费大量的时间。

(Q4)搜索方法如何选取?

八数码问题的解空间树属排列树,用于排列树搜索的方法主要有两大类:一类是盲目搜索,如深度优先搜索DFS和广度优先搜索BFS;另一类是启发式搜索,如A*算法。对于八数码问题,深度优先搜索的状态空间搜索树的深度可能很深,或者可能至少要比某个可接受的解答序列的已知深度上限还要深。应用此策略很可能得不到解。宽度优先搜索的优势在于当问题有解时,一定能找到解,且能找到最优解。但其搜索时需要保存所有的待扩展结点,这样很容易扩展那些没有用的结点,造成状态的指数增长,甚至"组合爆炸"。这对宽度优先搜索很不利。这两种搜索方法的共同缺点是结点排序杂乱无章,往往在搜索了大量无关结点后才能得到目的结点,只适合于复杂度较低的问题的求解。启发式搜索利用特定问题自身所携带的启发信息在一定程度上避免了盲目搜索的不足。

(Q5)如何选取评价函数?

对于f(n)的考虑最简单的便是比较每个状态与目标状态相比错位的个数。这个启发意味着如果其他条件相同,那么错位的个数最少的状态可能最接近目标状态。然而这个启发没有使用从棋盘格局中可以得到的所有信息,因为它没有把数字必要的移动距离纳入考虑。一个“更好一点”的启发是对错位的牌必须要移动的距离求和,为了达到这个目的,每张牌必须要移动的每个方格算为一个距离单位。本文采用后者。

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
/***************构造数据结构******************/
typedef struct Node
{
int a[9];   //棋盘的状态
int mark;   //该状态的标志(为比较两状态是否相同节省时间)
int dx;     //深度
int fx;      //评价函数值
struct Node* parent;  //标志父亲节点(为打印路径)
struct Node* prior;   //指示前驱
struct Node* next;    //方便查表
}Node,*SNode;              //棋盘状态节点
typedef struct StateQueue
{
SNode front;   //头指针
SNode rear;    //尾指针
}StateQueue;       //以棋盘状态为节点的状态队列
StateQueue closed,open;    //closed 表和 open表
/************相关子函数申明***************/
void copySNode(SNode &t,SNode s);
void initSQueue(StateQueue &Q , SNode s);
void markState(SNode s);
SNode searchSQueue(StateQueue Q,SNode s);
void exSNode(SNode t);
void childSolve(SNode s);
void evaluate(SNode s);
void insertOpen(SNode s);
void eightPuzzle();
void printPath(SNode s);
void openClosed(SNode s);
bool isOK();
SNode start,end;
/***************主函数****************/
int main()
{
printf("/****************八数码游戏**************/\n");
printf("问题描述:从给定初态达到目标状态:\n");
printf("1   2   3 \n");
printf("4   5   6  \n");
printf("7   8   0\n" );
printf("请给出求解路径!\n");
printf("/***************************************/");
printf("\n\n请输入初始状态:\n\n");
int i=0;
start=(SNode)malloc(sizeof(Node));
end=(SNode)malloc(sizeof(Node));
//freopen("in.txt","r",stdin);
for(i=0;i<9;++i)
{
scanf("%d",&start->a[i]);
end->a[i]=i+1;
}
end->a[8]=0;
start->dx=0;
if(!isOK())
{
printf("不能达到目的状态!!!\n");
return 0;
}
evaluate(start);
markState(start);
markState(end);
start->next=NULL;
start->parent=NULL;
start->prior=NULL;
eightPuzzle();
return 0;
}
/**************相关子函数*****************/
/**
*SNode节点的赋值
*/
void copySNode(SNode &t,SNode s)
{
int i;
t=(SNode)malloc(sizeof(Node));
for(i=0;i<9;++i)
{
t->a[i]=s->a[i];
}
t->dx=s->dx;
t->fx=s->fx;
t->mark=s->mark;
t->next=s->next;
t->parent=s->parent;
t->prior=s->prior;
}
/**
*初始化StateQueue
*/
void initSQueue(StateQueue &Q , SNode s)
{
Q.front=s;
Q.rear=s;
}
/**
*为棋盘状态mark赋值
*/
void markState(SNode s)
{
int i,mark=0;
s->mark=0;
for(i=0;i<8;++i)
{
mark=s->a[i];
s->mark+=mark<<(4*(7-i));
}
}
/**
*查StateQueue表,判断s节点是否存在并返回查到节点
*/
SNode searchSQueue(StateQueue Q,SNode s)
{
SNode q=Q.front;
if(!q)  return NULL;
do
{
if(q->mark==s->mark)
return q;
q=q->next;
}while(q);
return NULL;
}
/**
*扩展节点s并交由childSolve函数处理子节点
*/
void exSNode(SNode t)
{
int i;
SNode s;
for(i=0;i<9;++i)    //寻找空格位置
{
if(!t->a[i])  break;
}
if(i%3)      //空格向左移动
{
copySNode(s,t);
s->dx++;
s->parent=t;
s->a[i]=s->a[i-1];
s->a[i-1]=0;
markState(s);
childSolve(s);   //处理扩展的子节点
}
if(i%3!=2)   //空格向右移动
{
copySNode(s,t);
s->dx++;
s->parent=t;
s->a[i]=s->a[i+1];
s->a[i+1]=0;
markState(s);
childSolve(s);  //处理扩展的子节点
}
if(i/3)     //空格向上移动
{
copySNode(s,t);
s->dx++;
s->parent=t;
s->a[i]=s->a[i-3];
s->a[i-3]=0;
markState(s);
childSolve(s);  //处理扩展的子节点
}
if(i/3!=2)   //空格向下移动
{
copySNode(s,t);
s->dx++;
s->parent=t;
s->a[i]=s->a[i+3];
s->a[i+3]=0;
markState(s);
childSolve(s);  //处理扩展的子节点
}
}
/**
*处理子节点(即是,判断是否加入open表,插入位置)
*/
void childSolve(SNode s)
{
SNode p;
if(p=searchSQueue(open,s))    //在open表中
{
if(p->dx>s->dx)           //若子状态沿着更短的路径
{
s->fx=p->fx-p->dx+s->dx;
s->mark=p->mark;
if(!p->prior)    //表头
{
p->next->prior=NULL;
open.front=p->next;
}
else  if(!p->next)   //表尾
{
p->prior->next=NULL;
open.rear=p->prior;
}
else
{
p->next->prior=p->prior;
p->prior->next=p->next;
}
free(p);
}
else     return ;         //若子状态路径更长,不加入open表
}
else if(p=searchSQueue(closed,s))  //在closed表中
{
if(p->dx>s->dx)     //若子状态沿着更短路径,
{
s->fx=p->fx-p->dx+s->dx;
s->mark=p->mark;
if(!p->prior)    //表头
{
p->next->prior=NULL;
closed.front=p->next;
}
else  if(!p->next)   //表尾
{
p->prior->next=NULL;
closed.rear=p->prior;
}
else
{
p->next->prior=p->prior;
p->prior->next=p->next;
}
free(p);
}
else    return ;     //否则,不加入open表
}
else              //既不在open表,也不在closed表中
{
evaluate(s);    //评价该状态
}
insertOpen(s);    //把s插入open表中
}
/**
*evaluate评价该状态,即为求fx的值
*/
void evaluate(SNode s)
{
int i,value=0;
s->fx=s->dx;
for(i=0;i<9;++i)
{
if(!s->a[i])  value=8-i;
else       value=s->a[i]-i-1;
if(value>0)
{
s->fx+=value;
}
else   s->fx-=value;
}
}
/**
*insertOpen插入open表中
*/
void insertOpen(SNode s)
{
SNode p=open.front;
while(p->next&&p->fx<=s->fx)
{
p=p->next;
}
if(p->fx>s->fx)
{
s->prior=p->prior;    //更改前驱及后继指针
s->next=p;
if(p->prior)  p->prior->next=s;
else          open.front=s;
p->prior=s;
}
else
{
p->next=s;
s->prior=p;
s->next=NULL;
open.rear=s;
}
}
/**
*调试程序是用到的辅助函数
void  print()
{
SNode p;
p=open.front;
printf("\nopen:\n");
while(p)
{
printf("%.8x\n",p->mark);
p=p->next;
}
p=closed.front;
printf("\nclosed:\n");
while(p)
{
printf("%.8x\n",p->mark);
p=p->next;
}
}
**/
/**
*八数码问题求解策略
*/
void eightPuzzle()
{
initSQueue(open,start);
initSQueue(closed,NULL);
SNode s=open.front;
while(s->mark!=end->mark)
{
exSNode(s);
openClosed(s);    //把s从open表移到closed表的队尾
s=open.front;
}
printPath(s);
}
/**
*把s从open表移到closed表的队尾
*/
void openClosed(SNode s)
{
if(!s->prior)    //表头
{
s->next->prior=NULL;
open.front=s->next;
}
else  if(!s->next)   //表尾
{
s->prior->next=NULL;
}
else
{
s->next->prior=s->prior;
s->prior->next=s->next;
}
if(!closed.front)
{
closed.front=s;
closed.rear=s;
s->next=NULL;
s->prior=NULL;
}
else
{
if(!closed.front->next)  closed.front->next=s;
closed.rear->next=s;
s->prior=closed.rear;
s->next=NULL;
closed.rear=s;
}
}
/**
*打印出移动路径
*/
void printPath(SNode s)
{
SNode p=s;
int i,j;
p->next=NULL;
while(p->parent)
{
p->parent->next=p;
p=p->parent;
}
while(p)
{
for(i=0;i<3;++i)
{
for(j=i*3;j<3*(i+1);++j)
{
printf("%d  ",p->a[j]);
}
printf("\n");
}
printf("\n\n");
p=p->next;
}
}
/**
*判断初始状态能否到达目标状态
*/
bool isOK()
{
int i,j,count=0;    //count记录逆序数和
for(i=0;i<9;++i)
{
if(start->a[i])
{
for(j=i+1;j<9;++j)
{
if(start->a[j]&&start->a[j]<start->a[i])
{
count++;
}
}
}
}
if(!(count%2))   return true;
else             return false ;
}

不足之处:1)评价函数可以更加合理,提高搜索效率;2)本质是找出一系列状态,从start->end,end状态也可以随意变化。

注:改进方法还是比较简单的,希望看到此博文的人可以自行练习。

八数码问题的A*算法相关推荐

  1. 八数码问题及A*算法

    一.八数码问题 八数码问题也称为九宫问题.在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同.棋盘上还有一个空格,与空格相邻的棋子可以移到空格中.要求解决的问题是: ...

  2. 八数码问题a*算法c语言,八数码问题的A*算法求解

    A*算法是启发式搜素算法中较为出名和高效的算法之一,其关键是对于启发式函数的实际,启发式函数h(x)需要尽可能的接近实际的 .下面是人工智能八数码问题使用A*算法求解的源码放在博客上记录一下.程序使用 ...

  3. 算法提高课-搜索-A*(A star)算法-AcWing 179. 八数码:A星算法求解

    题目分析 来源:acwing 分析: A*算法是什么呢? A算法是一种bfs的变式,需要用到一个"估价函数",用来计算任意状态到目标状态所需代价的估计值.然后在搜索中,维护一个堆, ...

  4. 人工智能----八数码问题(启发式搜索)

    启发式搜索 一. 实验目的 理解人工智能系统中搜索策略的含义. 熟悉盲目搜索和启发式搜索算法的实际应用. 结合八数码问题的建模.求解及编程语言的应用,掌握启发式搜索算法的应用. 二. 实验原理 启发式 ...

  5. python---A*搜索算法解决八数码问题

    A*解决八数码问题 问题内容 算法流程 相关设置 具体程序 运行结果 遇到的问题 完结 问题内容 [八数码问题] 在一个3×3的九宫中有1-8这8个数字以及一个空格随机摆放在其中的格子里.将该九宫格调 ...

  6. AI 八数码A_star算法问题-实验报告

    一 题目要求: 八数码问题的A星搜索算法实现         要求:设计估价函数,并采用c或python编程实现,以八数码为例演示A星算法的搜索过程,争取做到直观.清晰地演示算法,代码要适当加注释. ...

  7. A*算法解决八数码问题 Java语言实现

    A*算法解决八数码问题 Java语言实现 参考文章: (1)A*算法解决八数码问题 Java语言实现 (2)https://www.cnblogs.com/beilin/p/5981483.html ...

  8. HUD 1043 Eight 八数码问题 A*算法 1667 The Rotation Game IDA*算法

    先是这周是搜索的题,网站:http://acm.hdu.edu.cn/webcontest/contest_show.php?cid=6041 主要内容是BFS,A*,IDA*,还有一道K短路的,.. ...

  9. 题目2:隐式图的搜索问题(A*算法解决八数码)代码实现

    从起点 开始,把它加入到一个由方格组成的open list(开放列表) 中,这个open list像是一个购物清单.Open list里的格子是可能会是沿途经过的,也有可能不经过.因此可以将其看成一个 ...

最新文章

  1. 2673(2673)shǎ崽 OrOrOrOrz
  2. Jquery 获取日期date()对象,jquerydate
  3. Python元组练习
  4. 云服务器ECS共享标准型S6全新发布, 行业内最具性价比
  5. 可以学习的国外课件链接地址(自己收集)
  6. Python学习笔记:使用Python操作数据库
  7. 机器学习sklearn
  8. foxmail本地文件夹同步服务器,foxmail同步QQ邮箱里的所有文件夹
  9. php自定义表单程序,自定义流程gooflow2.0+自定义表单
  10. spring-boot-starter-data-jpa详细使用介绍
  11. 拼音工具类PinyinUtils
  12. 祝酷狗猴年快乐,网易云称其耍猴
  13. Nginx静态Web服务搭建
  14. Listener method could not be invoked with the incoming messageEndpoint handler details:Method
  15. 学术之声 | 专访邵俊教授:区块链用技术保证在链上说话算话
  16. 【python文件读取】加密数据的读取
  17. 白帽子(5)- 命令注入与代码注入区别
  18. jenkins中使用脚本来节省资源空间和使用shell提取文件名或目录名的方法
  19. Gauss工作—学习笔记
  20. linux服务器性能阈值,linux – 如何根据可用内核的数量选择最大负载阈值?

热门文章

  1. 立创开源 51编程炫酷心形流水灯
  2. win7 引导 ubuntu
  3. 如何在 Facebook 上实现高效社交营销
  4. Hive之窗口函数(partition) / order by / row_number / date_sub 等函数联合使用案例(9)
  5. mm_cas登入失败
  6. 玲珑学院 1127 咸鱼文章
  7. 回顾知识点:计算机网络篇
  8. 内齿轮铣齿机铣削动力头的设计(论文+CAD图纸)
  9. MySQL启动报错:发生系统错误5。拒绝访问。
  10. python T检验