A*算法 启发式算法
package com.ex.cy.demo4.alg.graph.ewdi;
import com.ex.cy.demo4.alg.heap.IndexMinPQ;
import java.util.LinkedList;
import java.util.List;
//A* 算法
// 属于一种启发式搜索算法
// 和Djkstra有相似之处(和BFS)
//不同点在于,将周围顶点放入优先队列后的出队条件,不再是让g(v)(从起点到该点距离,v是被考察的顶点) 最小的先出队列
//而是让 g(v) + h(v) 最小的先出队
//h(v) 为 启发函数给出的值,用来估计v点到终点的估计距离,一般有1.欧几里得距离(sqrt((gx-vx)^2 + (gy-vy)^2)) 2.曼哈顿距离(abs(gx-vx)+abs(gy-vy)) (方格子,笛卡尔坐标系,只能上下左右)
//所以每次优先出队的是最接近到达终点最短距离的点(贪心,并不一定是最优解)
//循环退出条件是:只要遍历到终点则退出循环
public class AStart {
CoordinateEdgeWeightedDigraph cewd;
IndexMinPQ<Float> impq;
ASVertex[] vertices;
int s;
int g;
Edge[] fromEdge; //记录顶点V是从哪条边探索过来的,除了顶点,每个顶点有一条
//有向无环图,起点,终点
public AStart(CoordinateEdgeWeightedDigraph cewd, int s, int g) {
this.cewd = cewd;
this.s = s;
this.g = g;
vertices = new ASVertex[cewd.v()];
impq = new IndexMinPQ<>(cewd.v()); //最多有全图顶点,都被放进去过
fromEdge = new Edge[cewd.v()];
for (int i = 0; i < cewd.v(); i++) {
vertices[i] = new ASVertex(i, Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY);
}
vertices[s].g = 0;
vertices[s].h = 0;
astartSearch();
}
//启发函数
//估计从一个点到终点的曼哈顿距离
public float heuristicFunc(Vertex v1, Vertex v2) {
//欧几里得距离
// float dx = v2.x - v1.x;
// float dy = v2.y - v2.y;
// float dist = (float) Math.sqrt(dx * dx + dy * dy);
// return dist;
//曼哈顿距离(省去求平方,开根号,效率会比较快)
return Math.abs(v2.x - v1.x) + Math.abs(v2.y - v1.y);
}
public void astartSearch() {
impq.insert(s + 1, 0f); //v顶点索引, g(v)+h(v) 起点到该点距离 + 该点到终点估计距离
while (!impq.isEmpty()) {
int v = impq.topIndex() - 1;
float vdst = impq.delTop();
Vertex fromV = cewd.getVertex(v);
vertices[v].h = vdst;
if (v == g) //当前已到达终点
break; //停止探索
for (Edge e : cewd.adj(v)) {
int w = e.other(v);
if (vertices[w].h != Float.POSITIVE_INFINITY) //已探索过则跳过
continue;
Vertex toW = cewd.getVertex(w);
//到下一个顶点w的实际距离
float gDist = vertices[v].g + e.weight;
float heuDist = gDist + heuristicFunc(toW, fromV);
if (impq.contain(w + 1) && impq.get(w + 1) < heuDist)
continue; //包含到w的边,但距离未缩短,则跳过当前这条边
//新增或修改
fromEdge[w] = e;
vertices[w].g = gDist;
if (!impq.contain(w + 1)) { //优先队列中不存在,则直接插入新的顶点
impq.insert(w + 1, heuDist);
} else if (impq.get(w + 1) > heuDist) { //优先队列中存在,但以当前顶点v作为相邻点距离更小
impq.change(w + 1, heuDist); //更新距离
}
}
}
}
//返回从起点到终点的路径的权重总和
public float getCost() {
return vertices[g].g;
}
//获取从起点到终点的路径
//并不能像djkstra找到全局最优的最短路径,因为没有遍历其他可能的路径,因为第一次探索到终点就结束循环了
//但是优点是:找了个一个近似最优解,并用了较少的顶点探索次数,倾向性的往终点方向探索;因此可以用于很大的图中,不必遍历完所有顶点,即可找到一条近似最优解的路径,在时间效率和解的近似最优上达到了一种平衡
public List<Edge> getEdge() {
List<Edge> edges = new LinkedList<>();
int v = g;
Edge e = fromEdge[v];
while (e != null) {
edges.add(0, e);
v = e.other(v);
e = fromEdge[v];
}
return edges;
}
public static void main(String[] a) {
CoordinateEdgeWeightedDigraph cewd = new CoordinateEdgeWeightedDigraph(6);
//加入顶点
//顶点索引, x y 坐标
cewd.addVertex(0, 0, 0);
cewd.addVertex(1, 1, 0.5f);
cewd.addVertex(2, 0, 1);
cewd.addVertex(3, 1, 1);
cewd.addVertex(4, 5, 5);
cewd.addVertex(5, 6, 6);
//加入有向边,权重为两点之间的欧几里得距离
cewd.addEdge(0, 1);
cewd.addEdge(0, 2);
cewd.addEdge(1, 3);
cewd.addEdge(2, 3);
cewd.addEdge(3, 4);
System.out.println(cewd);
//测试1
AStart aStart = new AStart(cewd, 0, 4);
System.out.println(aStart.getEdge());
System.out.println("Cost: " + aStart.getCost());
//测试2 不可达顶点
AStart aStart2 = new AStart(cewd, 0, 5);
System.out.println(aStart2.getEdge());
System.out.println("Cost: " + aStart2.getCost());
}
}输出
Ver{v=0, x=0.0, y=0.0}: 0-1 1.12, 0-2 1.00,
Ver{v=1, x=1.0, y=0.5}: 1-3 0.50,
Ver{v=2, x=0.0, y=1.0}: 2-3 1.00,
Ver{v=3, x=1.0, y=1.0}: 3-4 5.66,
Ver{v=4, x=5.0, y=5.0}:
Ver{v=5, x=6.0, y=6.0}:
[0-1 1.12, 1-3 0.50, 3-4 5.66]
Cost: 7.274888
[]
Cost: Infinity 相关推荐
earthhouge 2017-03-29