写了下A*(读作A星,或者A Star)算法的地图搜索,作为一个算法新手来说,感觉真的很好啊,因为据说在使用一个合理的估价函数的前提下A*算法能够得到最优解。我会说在检查完运行结果后,我的内心不经涌起了一股成就感吗?
什么是A*算法,个人觉得A*算法是基于Dijkstra算法的一种优化,同样是基于广搜,并有一个当前列表检索列表,并不断的从优先队列中移除已完成节点。但是A*算法中多了一个开放队列,我想正是因为这个开放队列的存在,才使得A*算法能够求的最优解,因为最优解总是会包含在这个开放队列中的。
A*算法的标准特征是包含开放列表、关闭列表、估价函数等。
package com.wly.algorithmbase.search; import java.util.Comparator; import java.util.PriorityQueue; public class AStarAlgorithm { //包含通路的地图测试实例 private static int[][] graph = { {0,0,2,0,0,0,0,2,0,0}, {0,0,0,0,0,0,0,2,0,0}, {0,0,2,0,0,0,0,2,0,0}, {0,0,2,0,0,0,0,2,0,0}, {0,0,0,0,0,0,0,0,0,0}, {0,2,0,0,0,0,0,2,0,0}, {1,2,0,0,0,0,0,2,0,3}, {0,2,0,0,0,0,0,2,0,0}, {0,0,0,0,0,0,0,2,0,0} }; //不包含通路的地图测试实例 private static int[][] graph2 = { {0,0,2,0,0,0,0,2,0,0}, {0,0,0,0,0,0,0,2,0,0}, {0,0,2,0,0,0,0,2,0,0}, {0,0,2,0,0,0,0,2,0,0}, {0,0,0,0,0,0,0,0,0,0}, {0,2,0,0,0,0,0,2,0,2}, {1,2,0,0,0,0,0,2,2,2}, {0,2,0,0,0,0,0,2,0,3}, {0,0,0,0,0,0,0,2,0,0} }; static int start_row = 0; static int start_column = 0; static int dest_row = 0; static int dest_column = 0; private static S[][] enableFlag; //用来模拟"开放列表","关闭列表"的标记数组 private static enum S{ //对应三个状态:初始状态中,开放列表中,关闭列表中 INIT,OEPN,CLOSE }; private static int PRICE_HW = 10; //水平、垂直移动1单位代价 private static int PRICE_X = 14; //斜着移动1.44的代价 private final static int ROLD = 0; //通路 private final static int SELF = 1; //自己 private final static int STONE = 2; //障碍物 private final static int DEST = 3; //目标 public static void main(String[] args) { AStarAlgorithm aStarAlgorithm = new AStarAlgorithm(); //测试有解地图 System.out.println("---测试有解实例---"); Node[][] nodes = new Node[graph.length][graph[0].length]; enableFlag = new S[graph.length][graph[0].length]; for(int i=0;i<graph.length;i++) { for(int j=0;j<graph[0].length;j++) { nodes[i][j] = new Node(i,j,graph[i][j]); enableFlag[i][j] = S.INIT; //将状态置为初始状态 } } aStarAlgorithm.search(nodes); //测试无解地图 Node[][] nodes2 = new Node[graph.length][graph[0].length]; System.out.println("\n---测试无解实例---"); enableFlag = new S[graph2.length][graph2[0].length]; for(int i=0;i<graph2.length;i++) { for(int j=0;j<graph2[0].length;j++) { nodes2[i][j] = new Node(i,j,graph2[i][j]); enableFlag[i][j] = S.INIT; //将状态置为初始状态 } } aStarAlgorithm.search(nodes2); } /** * 开始搜索 * @param start_row 起始坐标的行号 * @param start_column 起始坐标的列号 */ public void search(Node[][] graph) { //打印地图数据 System.out.println("地图:"); for(int i=0;i<graph.length;i++) { for(int j=0;j<graph[0].length;j++) { System.out.print(graph[i][j].type + " "); } System.out.println(); } //检索地图得到起始点和目标点坐标 for(int i=0;i<graph.length;i++) { for(int j=0;j<graph[i].length;j++) { if(graph[i][j].type == SELF) { start_row = i; start_column = j; } else if(graph[i][j].type == DEST) { dest_row = i; dest_column = j; } } } //---使用java自带的优先队列--- Comparator<Node> comparator = new Comparator<Node>() { @Override public int compare(Node o1, Node o2) { if((o1.getCost()) > (o2.getCost())) { return 1; } else if((o1.getCost()) == (o2.getCost())) { return 0; } else { return -1; } } }; PriorityQueue<Node> queue = new PriorityQueue<>(12,comparator); //将起点放置到开放列表中 queue.add(new Node(start_row, start_column,1)); enableFlag[start_row][start_column] = S.OEPN; //将起点打上开放标记 //搜索不到通路(开放列表为空),或者搜索到了通路(终点位于开放列表中) while(!queue.isEmpty() && enableFlag[dest_row][dest_column] != S.OEPN) { Node currNode = queue.poll(); enableFlag[currNode.row][currNode.column] = S.CLOSE; //放到关闭列表 for(int i=currNode.row -1;i<currNode.row+2;i++) { for(int j=currNode.column-1;j<currNode.column+2;j++) { if(i>=0&& i<graph.length && j>=0 && j<graph[i].length //边界过滤 && graph[i][j].type != STONE //不是障碍物 && enableFlag[i][j] != S.CLOSE /*不在关闭列表中*/) { if(i != currNode.row && j != currNode.column) { //斜着移动 if(graph[i][j].getCost() > currNode.getCost() + PRICE_X) { graph[i][j].setG(currNode.getG() + PRICE_X); } } else { //水平、垂直移动 if(graph[i][j].getCost() > currNode.getCost() + PRICE_HW) { graph[i][j].setG(currNode.getG() + PRICE_HW); } } if(enableFlag[i][j] == S.INIT) { queue.add(graph[i][j]); enableFlag[i][j] = S.OEPN; } } } } System.out.print(currNode.row + "," + currNode.column + "=>"); } if(queue.isEmpty()) { System.out.println("\n---未搜索到从起点到终点的有效通路!"); } else { System.out.print(dest_row + "," + dest_column); System.out.println("\n---搜索到从起点到终点的有效通路!"); } } /** * 节点类 * @author wly * */ static class Node { Node parentNode; //持有父节点的引用 int g; //搜索当前节点已发生的开销 int row,column; //坐标 int type; //当前节点的类型(障碍物、道路等) public Node(int row, int column,int type) { super(); this.row = row; this.column = column; this.type = type; } public int getG() { return g; } public void setG(int g) { this.g = g; } public int getCost() { return getHValue() + g; } /** * 估算函数 * @return */ private int getHValue() { return (Math.abs(row - dest_row) + Math.abs(column - dest_column)) * PRICE_HW; } } }运行结果:
---测试有解实例--- 地图: 0 0 2 0 0 0 0 2 0 0 0 0 0 0 0 0 0 2 0 0 0 0 2 0 0 0 0 2 0 0 0 0 2 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 2 0 0 1 2 0 0 0 0 0 2 0 3 0 2 0 0 0 0 0 2 0 0 0 0 0 0 0 0 0 2 0 0 6,0=>5,0=>7,0=>4,1=>5,2=>6,3=>6,4=>6,5=>6,6=>5,6=>7,6=>4,7=>5,8=>6,9 ---搜索到从起点到终点的有效通路! ---测试无解实例--- 地图: 0 0 2 0 0 0 0 2 0 0 0 0 0 0 0 0 0 2 0 0 0 0 2 0 0 0 0 2 0 0 0 0 2 0 0 0 0 2 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 0 0 0 0 2 0 2 1 2 0 0 0 0 0 2 2 2 0 2 0 0 0 0 0 2 0 3 0 0 0 0 0 0 0 2 0 0 6,0=>7,0=>8,1=>7,2=>7,3=>7,4=>7,5=>7,6=>6,6=>8,6=>5,6=>4,7=>5,8=>4,9=>4,8=>3,9=>2,9=>3,8=>1,9=>8,5=>6,5=>4,6=>5,5=>8,4=>2,8=>5,4=>0,9=>4,4=>5,3=>6,4=>1,8=>4,5=>8,3=>6,3=>3,6=>2,6=>2,5=>5,2=>3,4=>0,8=>1,6=>8,2=>3,5=>6,2=>4,3=>8,0=>2,4=>0,6=>4,2=>3,3=>1,5=>5,0=>0,4=>4,0=>3,1=>1,4=>2,3=>0,5=>4,1=>2,1=>0,3=>3,0=>1,2=>1,1=>1,3=>2,0=>0,1=>1,0=>0,0=> ---未搜索到从起点到终点的有效通路!说明几点:
1、代码中用一个状态标记和枚举变量代替了当前节点出于"开放列表","关闭列表"的状态。
2、代码中使用了Java自带的PriorityQueue来做优先队列,这个PriorityQueue是在JDK1.7中新增的,若在1.6的JDK上会报错。当然,读者也可以参考笔者的另一篇博文:使用二叉堆实现优先队列。
3、A*的关键在于"开放队列"的思想以及估价函数的建立,估价函数不能太大,这样会导致搜索速度不高,也不能太小,因为这样可能导致搜索不到最优解。
4、And,如果你对A*算法还不是很了解,笔者这里有秘籍:猛戳下载。
O啦~~~
转载请保留出处:http://blog.csdn.net/u011638883/article/details/17245371
谢谢!!