A*算法求解8数码问题

来源:互联网 发布:json 汉字解析不出来 编辑:程序博客网 时间:2024/05/23 22:29

        困扰我多日的八数码问题终于解决了,一度对八数码问题不知道该如何下手,网上很多都是用A*算法解的,但是版本可以说各有千秋,自己一时间看看各个版本的代码,也弄的头昏脑涨的,这两天一直研究A*算法,然后想通过一个实例来好好学习下A*问题,这样如果能够很好的解决典型的8数码问题,对自己也有个很好的提升。在网上看到的版本大部分都是c++的,然后我想自己就通过java来实现,毕竟用java来实现算法要容易的多,同时也易于理解。

       我想从以下几个方面来阐述解决8数码问题的方法

     1 了解A*算法

     2 A*算法的伪代码

    3 如何在8数码问题中应用A*算法(即建立合适的启发式)

    4 8数码问题比较特殊,需要判断是否有可行解

    5 实现A*算法在8数码问题中的应用

1 了解A*算法

    在网上看了很多文章,发现一篇感觉很好的阐述了A*算法的。连接如下:A*算法

   1,从起始状态A开始,并且把它作为待处理点存入一个“开启列表”。开启列表就像一张购物清单。尽管现在列表里只有一个元素,但以后就会多起来。你的路径可能会通过它包含的其他状态,也可能不会。基本上,这是一个待检查方格的列表。
   2,寻找起点周围通过上下左右移动所有可到达的状态。也把他们加入开启列表。为所有这些状态保存状态A作为“父方格”。当我们想描述路径的时候,父方格的资料是十分重要的。后面会解释它的具体用途。
   3,从开启列表中删除状态A,把它加入到一个“关闭列表”。

  4 取出开启列表中fvalue最低的状态,判断该状态性质

    (1) 如果在开启列表中出现,那么判断该状态的gvalue通过当前节点(从open中取出,扩展出该节点的节点)作为父节点带来的gvalue 是否小于原先的父节点,若小于则需要更改它的父节点以及gvalue.

    (2) 如果在关闭列表中出现,那么判断该状态的gvalue通过当前节点(从open中取出,扩展出该节点的节点)作为父节点带来的gvalue是否小于原先的父节,若小于则需要从关闭列表中删除该节点,并且将其加入开启列表,并更改其父节点为当前节点。

    (3) 既不在开启列表,也不在关闭列表,这样就很简单,直接加入开启列表中就行(保持开启列表升序排列)

    (4) 如果取出的节点为目标节点,则成功退出

2 A*算法的伪代码

 创建两个表,OPEN表保存所有已生成而未考察的节点,CLOSED表中记录已访问过的节点。 

 算起点的估价值;

 将起点放入OPEN;

    while(OPEN!=NULL)

  {

    从OPEN表中取估价值f最小的节点n;

          if(n节点==目标节点){

          break;

           }

          for(当前节点n的每个子节点X)

        {

          算X的估价值;

               if(X in OPEN)

             {

                 if( X的估价值小于OPEN表的X估价值 ){

                把n设置为X的父亲;

               更新OPEN表中的估价值; //取最小路径的估价值 

                      }

           }

         if(X inCLOSE) {

                 if( X的估价值小于CLOSE表的X估价值 ){

                把n设置为X的父亲;

          
                  将
该节点从close表中除去

             把X节点放入OPEN //取最小路径的估价值 

                        }

               }

          if(X not inboth){

           把n设置为X的父亲;

           求X的估价值;

           并将X插入OPEN表中; //升序排列open

          }

     }//end for

  将n节点插入CLOSE表中;

  按照估价值将OPEN表中的节点排序; //实际上是比较OPEN表内节点f的大小,从最小路径的节点向下进行。

   }//end while(OPEN!=NULL)

 保存路径,即从终点开始,每个节点沿着父节点移动直至起点,这就是你的路径;


  3  如何在8数码问题中应用A*算法(即建立合适的启发式)

    A*算法有个计算公式 f(x) = g(x)+h(x)

    其中g(x)为从起始状态到当前状态所消耗的步数,h(x)为一个启发值,估算从当前状态到目标状态所需的步数,一般h(x)小于等于实际需要步数为好,这样不会将最优解忽略,因为h(x)和解空间有一些关系,如果h(x)设置的比实际需要的步数多,那么解空间就有可能将最优解忽略。举个例子吧,宽度优先搜索就是h(x)=0带来的效果,深度优先搜索就是g(x)=0带来的效果不过h(x)距离h*(x)[实际需要的步数]的程度不能过大,否则h(x)就没有过强的区分能力,算法效率并不会很高。对一个好的h(x)的评价是:h(x)h*(n)[实际需要的步数]的下界之下,并且尽量接近h*(n)[实际需要的步数].

   那么8数码问题g(x) 为经过上下左右移动空格附近的数字来得到新状态所需步数,h(x)为当前状态与目标状态的距离,就是所有不在目标位置的数字总和,必然小于h*(x)


4 8数码问题比较特殊,需要判断是否有可行解

  网上的一般解法,就是通过逆序数的奇排列偶排列判断,可以在这看到逆序数介绍

 当目标状态的奇偶性质和初始状态的奇偶性质相同时,才能够进行转换,具体原理我不清楚,还需要查查。

5 实现A*算法在8数码问题中的应用

import java.util.ArrayList;import java.util.Comparator;import java.util.Iterator;import java.util.List;import java.util.PriorityQueue;public class astar {public static void main(String args[]){int[][] startStatus={{2,3,1},{5,0,8},{4,6,7}}; //起始状态int[][] endStatus = {{1,2,3},{4,5,6},{7,8,0}}; //结束状态//int[][] endStatus = {{2,3,1},{5,8,7},{4,6,0}};AstarDoer test = new AstarDoer(startStatus,endStatus);test.run();}}class AstarDoer{int[][] startStatus;int[][] endStatus;NodeComparator cmp = new NodeComparator();  PriorityQueue<Node> open = new PriorityQueue<Node> (1000000,cmp);  PriorityQueue<Node>  close =new PriorityQueue<Node> (1000000,cmp);  public AstarDoer(int[][] startStatus,int[][] endStatus ){this.startStatus = new int[3][3];this.endStatus = new int[3][3];for(int i=0;i<3;i++){for(int j=0;j<3;j++){this.startStatus[i][j] = startStatus[i][j];this.endStatus[i][j] = endStatus[i][j];}}}private int getReverse(int[][] status) //获得逆序数奇偶性{int reverse=0;for(int i=0;i<9;i++){for(int j=i+1;j<9;j++){int k = i/3;int m = i%3;if(status[k][m]>status[j/3][j%3]){reverse+=1;}}}return reverse;}private boolean check(int[][] startStatus,int[][] endStatus){//判断是否有可行解int getS =0;int getE =0;getS = getReverse(startStatus);getE = getReverse(endStatus);if((getS%2) == (getE%2) ){return true;}return false;}private void initStart(){//将开始节点加入open列表Node startNode = new Node(startStatus);startNode.gvalue = 0;startNode.parent = null;startNode.hvalue = startNode.getH(endStatus);startNode.fvalue = startNode.gvalue+startNode.hvalue;open.add(startNode);}private boolean isInList(Node lNode,Node newNodeParent){//判断该节点是否已经在之前出现过,之前出现的g值 肯定小于现在的g值,因为都是通过//同一节点经过上下左右变换得到while(newNodeParent != null){if(lNode.equal(newNodeParent.status)){return true;}newNodeParent = newNodeParent.parent;}return false;}private void initChild(Node newNode,List<Node> newNodeChild ){//生成该节点周围的子节点int whiteSpace = 0;whiteSpace = newNode.getWhitespace();/*得到空格位置判断左移产生节点*/if((whiteSpace%3) !=2){Node lNode = new Node(newNode.status);lNode.status[whiteSpace/3][whiteSpace%3] = lNode.status[whiteSpace/3][(whiteSpace%3)+1];lNode.status[whiteSpace/3][(whiteSpace%3)+1] = 0;if(isInList(lNode,newNode.parent) == false){lNode.parent = newNode;lNode.gvalue = newNode.gvalue+1;lNode.hvalue = lNode.getH(endStatus);lNode.fvalue = lNode.gvalue+lNode.hvalue;newNodeChild.add(lNode);}}/*得到空格位置判断右移产生节点*/if((whiteSpace%3) !=0){Node lNode = new Node(newNode.status);lNode.status[whiteSpace/3][whiteSpace%3] = lNode.status[whiteSpace/3][(whiteSpace%3)-1];lNode.status[whiteSpace/3][(whiteSpace%3)-1] = 0;if(isInList(lNode,newNode.parent) == false){lNode.parent = newNode;lNode.gvalue = newNode.gvalue+1;lNode.hvalue = lNode.getH(endStatus);lNode.fvalue = lNode.gvalue+lNode.hvalue;newNodeChild.add(lNode);}}/*得到空格位置判断上移产生节点*/if((whiteSpace/3) !=2){Node lNode = new Node(newNode.status);lNode.status[whiteSpace/3][whiteSpace%3] = lNode.status[whiteSpace/3+1][whiteSpace%3];lNode.status[whiteSpace/3+1][whiteSpace%3] = 0;if(isInList(lNode,newNode.parent) == false){lNode.parent = newNode;lNode.gvalue = newNode.gvalue+1;lNode.hvalue = lNode.getH(endStatus);lNode.fvalue = lNode.gvalue+lNode.hvalue;newNodeChild.add(lNode);}}/*得到空格位置判断下移产生节点*/if((whiteSpace/3) !=0){Node lNode = new Node(newNode.status);lNode.status[whiteSpace/3][whiteSpace%3] = lNode.status[whiteSpace/3-1][whiteSpace%3];lNode.status[whiteSpace/3-1][whiteSpace%3] = 0;if(isInList(lNode,newNode.parent) == false){lNode.parent = newNode;lNode.gvalue = newNode.gvalue+1;lNode.hvalue = lNode.getH(endStatus);lNode.fvalue = lNode.gvalue+lNode.hvalue;newNodeChild.add(lNode);}}}private boolean isOPenOrCloseIn(Node newNode,PriorityQueue<Node> openList){Iterator<Node> iter = openList.iterator();while(iter.hasNext()){Node iterNode = iter.next();if(iterNode.equal(newNode.status)){return true;}}return false;}public void run(){if(check(startStatus,endStatus)== false ){System.out.println("can not change to that");return;}initStart();while(open.isEmpty()==false){Node newNode = open.poll();close.add(newNode);if(newNode.equal(endStatus)){int i=0;System.out.println("success finish this Task~~~~~");while(newNode != null){for(int j=0;j<9;j++){System.out.print(newNode.status[j/3][j%3]);System.out.print("  ");if(j%3 == 2){System.out.println();}}System.out.println("step "+i+"status fvalue"+newNode.fvalue);newNode = newNode.parent;i+=1;}break;}List<Node> newNodeChild = new ArrayList<Node>();initChild(newNode,newNodeChild);for(Node iter:newNodeChild){if(isOPenOrCloseIn(iter,open) == true){Iterator<Node> iterNew = open.iterator();while(iterNew.hasNext()){Node iterNode = iterNew.next();if(iterNode.equal(iter.status)){if(iter.gvalue < iterNode.gvalue){iterNode.parent = iter.parent;iterNode.gvalue = iter.gvalue;iterNode.fvalue = iter.fvalue;}}}}else if(isOPenOrCloseIn(iter,close)==true){Iterator<Node> iterNew = close.iterator();while(iterNew.hasNext()){Node iterNode = iterNew.next();if(iterNode.equal(iter.status)){if(iter.gvalue < iterNode.gvalue){iterNode.parent = iter.parent;iterNode.gvalue = iter.gvalue;iterNode.fvalue = iter.fvalue;close.remove(iterNode);open.add(iter);}}}}else{open.add(iter);}}}} class NodeComparator implements Comparator<Node> {          @Override          public int compare(Node x, Node y) {              return x.fvalue - y.fvalue;          }      }  }class Node{Node parent;public int[][] status = new int[3][3];int fvalue;int gvalue;int hvalue;public Node(int[][] status){for(int i=0;i<3;i++){for(int j=0;j<3;j++){this.status[i][j] = status[i][j];}}}public void setG(int gvalue){this.gvalue = gvalue;}public int getWhitespace(){int k=0;for(int i=0;i<9;i++){if(status[i/3][i%3] == 0){k=i;return k;}}return k;}public boolean equal(int[][] endStatus){for(int i=0;i<3;i++){for(int j=0;j<3;j++){if(status[i][j]!= endStatus[i][j]){return false;}}}return true;}public int getH(int[][] endStatus){int k=0;for(int i=0;i<3;i++){for(int j=0;j<3;j++){if(status[i][j] != endStatus[i][j]){k+=1;}}}return k;}}

  以上就是我对8数码问题的分析和解决过程,如果有问题,希望大家共同交流,终于解决了一个大难题,后面开始新的学习,加油~