A*算法解决八数码问题

以下内容仅是我个人对八数码问题和A*寻路算法的理解,因为我是菜鸟一个,所以写的比较通俗。 

八数码问题也称为九宫问题。在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以移到空格中。要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。

A*算法:

     A*算法是一种在静态路网中求解最短路径的有效算法,通俗地讲,它不是像深度优先搜索算法和广度优先搜索算法一样的傻瓜式的埋头搜索,它是先对当前的情况进行分析,得到最有可能的一个分支,然后在该分支上进行扩展,然后将扩展的结果放在之前的大环境中进行比较,再选取最有可能的分支进行扩展,直到找到最终状态。A*算法的核心是估价函数的选取(通俗的说就是对当前情况的评价方式的选取,通过什么方式选取的分支才是最有可能离最终状态最近的分支)。

公式表示为: f(n)=g(n)+h(n),
其中 f(n) 是从初始点经由节点n到目标点的估价函数,
g(n) 是在状态空间中从初始节点到n节点的实际代价,
h(n) 是从n到目标节点最佳路径的估计代价。

用A*算法思想分析八数码问题:

在本题目中,初始状态为任意一个可能的状态。终止状态为按顺序排列的八个数,空白用0表示,即: 在八数码问题中,节点即为一个状态,我们令g(n)为从开始节点到当前节点所经过的实际步数(即深度),h(n)为从该节点到最终节点必须至少要的步数的估计(在估算h(n)时,我们忽略其他的数字对该数字到其最终位置的影响,所以h(n)只是其最小步数的下限),f(n)=h(n)+g(n)。

八数码问题的C++代码:

#include<iostream>
#include <stdlib.h>
using namespace std;
struct Node //节点
 {
   int s[3][3];
   int f,g;   //f为从当前节点到最终节点的最少代价,g为从初始状态到当前状态经过的步数
   Node * next,*previous;
 };
 class EP
 {
 private:
    Node *open,*close,*bestnode,*successor,*initial;//open为没有扩展过的能到达的状态的链表头,close为扩展过的的节点的链表表头
                                                    //bestnode为当前评价最好的节点,即g+f最小的节点
                                                    //successor为有当前节点经过一步操作可能到达的节点
                                                    //initial为初始节点
    int errorsum(int[3][3]);                        //计算f的函数,即节点上的所有数字到其最终位置的哈密顿距离的和
    void addnode(Node* &,Node*);                    //向表中加入节点
    void removenode(Node *&,Node*);                 //从表中删除节点
    bool getmove(char,Node*);                       //求由当前节点经过一步操作可能到达的下一个节点
    void update(Node*);                             //数据更新(因为到达一个节点可不同的路径,我们只要记住最短的路径即可,所以
                                                    //当出现的节点和之前评价过的节点相同且当前的代价要比之前代价小时,我们要放
                                                    //弃之前的评价更新为现在的评价。
    int go();                                       //求解入口
    bool iscontain(Node*,Node*,Node*);              //求表中是否含有某节点
    void show(Node*);                               //输出最终路径
 public:
    EP();                                           //构造函数
 };
 EP::EP()
 {
     open=new Node;
     open->previous=NULL;
     open->next=NULL;
     close=new Node;
     close->previous=NULL;
     close->next=NULL;
     for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
         open->s[i][j]=close->s[i][j]=0;
     cout<<"输入初始状态:"<<endl;
     initial=new Node;
     initial->previous=NULL;
     initial->next=NULL;
     for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
          cin>>initial->s[i][j];
     initial->f=errorsum(initial->s);
     initial->g=0;
     addnode(open,initial);
     if(go()==-1)
        cout<<"无解"<<endl;
     else
        show(bestnode);
 }
 int EP::errorsum(int s[3][3])
 {
     int sum=0;
     for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
            if(s[i][j]!=0)
          sum+=(abs((s[i][j]-1)/3-i)+abs((s[i][j]-1)%3-j));
     return sum;
 }
 void EP::addnode(Node* &h,Node*p)
 {
     Node *q=h->next;
     if(q)
     {
         if((p->f+p->g)<(q->f+q->g))
         {
             p->next=q;
             h->next=p;
         }
         else
         {
             while(q->next)
             {
                 if((p->f+p->g)<(q->f+q->g)&&(q->next->f+q->next->g)>=(p->f+p->g))
                 {
                     p->next=q->next;
                     q->next=p;
                     break;
                 }
                 q=q->next;
             }
             if(q->next=NULL)
                q->next=p;
         }
         }

     else
        h->next=p;
 }
 void EP::removenode(Node* &h,Node*p)
 {
     Node*q=h;
     while(q->next)
         {
             if(q->next==p)
             {
                 q->next=p->next;
                 if(q->next==NULL)
                    return;
             }
             q=q->next;
         }
 }
 int EP::go()
 {
     while(1)
     {
         if(open->next==NULL)
            return -1;
         bestnode=open->next;
         removenode(open,bestnode);
         addnode(close,bestnode);
         if((bestnode->f==0))
             return 1;
         else
         {
             successor=new Node;
             if(getmove('d',successor))
                 update(successor);
             successor=new Node;
             if(getmove('u',successor))
                 update(successor);
             successor=new Node;
             if(getmove('l',successor))
                 update(successor);
             successor=new Node;
             if(getmove('r',successor))
                 update(successor);
         }
     }
 }
 bool EP::getmove(char c,Node*p)
 {
     int i0,j0;
     for(int i=0;i<3;i++)
        for(int j=0;j<3;j++)
        {
            p->s[i][j]=bestnode->s[i][j];
            if(p->s[i][j]==0)
               i0=i,j0=j;
        }
     switch(c)
     {
     case 'u':if(i0<2){p->s[i0][j0]=p->s[i0+1][j0],p->s[i0+1][j0]=0;return true;}break;
     case 'd':if(i0>0){p->s[i0][j0]=p->s[i0-1][j0],p->s[i0-1][j0]=0;return true;}break;
     case 'l':if(j0<2){p->s[i0][j0]=p->s[i0][j0+1],p->s[i0][j0+1]=0;return true;}break;
     case 'r':if(j0>0){p->s[i0][j0]=p->s[i0][j0-1],p->s[i0][j0-1]=0;return true;}break;
     }
     return false;
 }
 void EP::update(Node *p)
 {
     Node* old;
     p->previous=bestnode;
     p->next=NULL;
     p->g=bestnode->g+1;
     p->f=errorsum(p->s);
     if(iscontain(open,p,old))
     {
         if(p->g<old->g)
         {
             old->g=p->g;
             removenode(open,old);
             addnode(open,old);
         }
     }
     else
     {
         if(iscontain(close,p,old))
     {
          if(p->g<old->g)
         {
             old->previous=bestnode;
             old->g=p->g;
         }
     }
     else
     {
         p->f=errorsum(p->s);
         addnode(open,p);
     }
     }
 }
 bool EP::iscontain(Node *h,Node*p,Node*old)
 {
     Node* q=h;
     while(q->next)
     {
         int f=0;
         for(int i=0;i<3;i++)
            for(int j=0;j<3;j++)
              if(q->next->s[i][j]!=p->s[i][j])
                f=1;break;
         if(f==0)
         {
             old=q->next;
             return true;
         }
         else
            q=q->next;
     }
     return false;
 }
 void EP::show(Node* h)
 {
   Node *p=h;
   while(p!=initial)
       p->previous->next=p,p=p->previous;
   p=initial;
   while(p!=h->next)
   {
       cout<<"---------------------"<<endl;
       for(int i=0;i<3;i++)
       {
           for(int j=0;j<3;j++)
            cout<<p->s[i][j]<<" ";
           cout<<endl;
       }
        cout<<"f="<<p->f<<"  g="<<p->g<<endl;
        p=p->next;
   }
 }
int main()
{
    EP e;
    return 0;
}

需要注意的是:

1.open是一个单向链表,存储的是可以由初始节点由若干步达到的可能节点(我们忽略节点Node中的previous),其是按照代价估计(f+g)从小 到大排序的。

2.close的结构比较特殊,如果从头到尾顺序看(忽略节点Node中的previous)是一个单向链表,从尾向前看(忽略节点Node中的next),是一个倒向的树。(因为要输出最终的节点转移过程,所以必须要记录每一个节点是由哪一个节点扩展来的,虽然一个节点可以由很多节点扩展而来,但是别忘了我们之前说的,只记录从初始节点到当前节点最短的那条路径,update函数所做的事),虽然结构有点混乱,但是顺看的链表和倒看的倒树并不相互影响,链表只是为了方便节点的管理。

3.最后输出结果的过程弄乱了close的结构,只保留了我们要输出的这条路径线。

该代码段的缺点:

内存回收比较难,内存都是只申请不释放。

我们求得的只是较短的路径,但未必是最短的路径。

时间: 2024-08-01 06:39:28

A*算法解决八数码问题的相关文章

人工智能作业homework2--------A*算法解决八数码

1.启发式搜索算法A 启发式搜索算法A,一般简称为A算法,是一种典型的启发式搜索算法.其基本思想是:定义一个评价函数f,对当前的搜索状态进行评估,找出一个最有希望的节点来扩展. 评价函数的形式如下: f(n)=g(n)+h(n) 其中n是被评价的节点. f(n).g(n)和h(n)各自表述什么含义呢?我们先来定义下面几个函数的含义,它们与f(n).g(n)和h(n)的差别是都带有一个"*"号. g*(n):表示从初始节点s到节点n的最短路径的耗散值: h*(n):表示从节点n到目标节点

【转】A*算法解决八数码问题

from utils import ( PriorityQueue) import copy infinity = float('inf') def best_first_graph_search(problem, f): #定义初始节点 node = Node(problem.initial) node.fvalue=f(node) #如果是最终结果,返回节点 if problem.goal_test(node): return node #frotier是一个顺序队列,从小到大排列,排列比较

每天刷个算法题20160526:BFS解决八数码问题(九宫格问题)

版权所有.所有权利保留. 欢迎转载,转载时请注明出处: http://blog.csdn.net/xiaofei_it/article/details/51524864 为了防止思维僵化,每天刷个算法题.已经刷了几天了,现在发点代码. 我已经建了一个开源项目,每天的题目都在里面: https://github.com/Xiaofei-it/Algorithms 绝大部分算法都是我自己写的,没有参考网上通用代码.读者可能会觉得有的代码晦涩难懂,因为那是我自己的理解. 最近几天都是在写一些原来的东西

【算法】BFS+哈希解决八数码问题

15拼图已经有超过100年; 即使你不叫这个名字知道的话,你已经看到了.它被构造成具有15滑动砖,每一个从1到15上,并且所有包装成4乘4帧与一个瓦块丢失.让我们把丢失的瓷砖"X"; 拼图的目的是安排瓷砖以便它们排序为: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15× 这里唯一合法经营是交流'X'与它共享一个边缘的瓷砖之一.作为一个例子,举动下列顺序解决了一个稍微加扰难题: 1 2 3 4 1 2 3 4 1 2 3 4 1 2 3 4 5 6 7 8 5 6

有很多种方法来解决八数码

AI实验报告,改变了重定向.希望通过翼牛. 我很纳闷ida*然而,如何快速的双搜索.还找到了灵感不在位的基础上A*和Ida*来到慢.特别ida* 搜索31步骤甚至十几秒.我写的代码是有问题?忘记丹尼尔路过指点啊.!! ! 另外声明一下,有些东西也是看网上各路牛人的blog学来的,因为比較杂,再次无法一一列出.总之再次感谢把自己的思考的结果放到网上与大家分享的大牛们.谢谢! 八数码问题 八数码问题也称为九宫问题.在3×3的棋盘,摆有八个棋子,每一个棋子上标有1至8的某一数字.不同棋子上标的数字不同

广度优先搜索解决八数码问题

八数码简介 八数码问题也称为九宫问题.在3×3的棋盘,摆有八个棋子,每一个棋子上标有1至8的某一数字,不同棋子上标的数字不同样.棋盘上另一个空格,与空格相邻的棋子能够移到空格中.要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤.所谓问题的一个状态就是棋子在棋盘上的一种摆法.棋子移动后,状态就会发生改变.解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态. 求解八数码问题要懂得的知识 1.康托展开,八数码在交换的过程

A*算法实现 八数码问题

有关八数码问题及其参考: http://wenku.baidu.com/view/87c92ef1ba0d4a7302763a29.html http://blog.csdn.net/damotiansheng/article/details/40017107 http://blog.csdn.net/wsywl/article/details/5726617 下面的代码实现可以在poj上验证其正确性,估价函数可以进行修改. poj 原题网址:http://bailian.openjudge.c

BFS解决八数码问题和狼人过河问题

1.八数码问题 问题描述: 初态: 0    1    2 3    4    5 6    7    8 如何移动交换0的位置达到终态 1    2     3 4    5     6 7    8     0 思路如下: 先将图转换为一个整数 初态:876543210终态:087654321 构造状态的数据结构 struct node{int x;int where0;} 运动规则如下 switch where0:case0: d,rcase1: d,l,rcase2: d,lcase3:

八数码问题解析

八数码的问题描述为: 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用-1来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初始状态)和目标布局,找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变. 解决八数码的方法很多,本文采用1.广度优先搜索的策略,和A星算法两种比较常用的算法思想解决此问题 广度优先搜索的策略一般可以描述为以下过程: 状态空间的一般搜索过程 OPEN表:用于存放刚生成的节点 CLOSE表:用于存放将要扩