属于一种best-first search,通过不断选择当前预期距离最小的点来实现。
参考:
http://www.cs.cmu.edu/~cga/ai-course/astar.pdf
https://en.wikipedia.org/wiki/A*_search_algorithm
标记
g
(
n
)
=
d
i
s
t
a
n
c
e
[
s
t
a
r
t
]
[
n
]
g(n) = distance[start][n]
g(n)=distance[start][n], (当前计算得到的准确值)
h
(
n
)
=
d
i
s
t
a
n
c
e
[
n
]
[
e
n
d
]
h(n) = distance[n][end]
h(n)=distance[n][end] (由heuristic函数决定)
f
(
n
)
=
g
(
n
)
+
h
(
n
)
f(n) = g(n) + h(n)
f(n)=g(n)+h(n)
算法
- 每轮取 f ( n ) f(n) f(n)最小的点
- 遍历neighbor,如果当前经过 n n n到neighbor的路径更优,更新他们的 g , f g,f g,f值
代码
public class Astar {
public static void main(String[] args) {
double[][] graph = new double[6][6];
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 6; j++) {
graph[i][j] = Double.MAX_VALUE;
}
}
graph[0][1] = 1;
graph[0][2] = 1;
graph[2][3] = 1;
graph[1][3] = 0.5;
graph[3][4] = 1;
graph[4][5] = 7;
int[] h = new int[] {8, 7, 3, 2, 1, 0};
for (int i : astar(0, 5, graph, h)) {
System.out.print(i + " ");
}
}
/**
*
* @param start start vertex
* @param end end vertex
* @param graph adjacency matrix, value as edge weight
* @param h heuristics
* @return shortest path from start to end
*/
public static List<Integer> astar(int start, int end, double[][] graph, int[] h) {
double[] g = new double[graph.length];
Arrays.fill(g, Double.MAX_VALUE);
g[start] = 0;
double[] f = new double[graph.length];
Arrays.fill(f, Double.MAX_VALUE);
f[start] = h[start];
int[] parent = new int[graph.length]; // to reconstruct path
Arrays.fill(parent, -1);
PriorityQueue<Integer> pq = new PriorityQueue<>((o1, o2) -> Double.compare(f[o1], f[o2]));
pq.add(start);
while(!pq.isEmpty()) {
int current = pq.poll();
if (current == end) {
return reconstruct(parent, current);
}
System.out.println("current " + current + " f " + f[current]);
for (int i = 0; i < graph.length; i++) { // loop through the neighbors
if (graph[current][i] != Double.MAX_VALUE) { // connected
double temp = g[current] + graph[current][i];
if (temp < g[i] && g[current] != Double.MAX_VALUE) { // this path is shorter
parent[i] = current;
g[i] = temp;
f[i] = g[i] + h[i];
}
if (!pq.contains(i)) {
pq.add(i);
}
}
}
}
return null; // no path available
}
public static List<Integer> reconstruct(int[] parent, int current) {
List<Integer> list = new LinkedList<>();
while (current != -1) {
list.add(0, current);
current = parent[current];
}
return list;
}
}
Output:
current 0 f 8.0
current 2 f 4.0
current 3 f 4.0
current 4 f 4.0
current 1 f 8.0
current 3 f 3.5
current 4 f 3.5
0 1 3 4 5
例子
准确性
A*的准确性依赖于heuristic的选择。在两个条件下保证最优。
- Consistency(较强)
对任意边 ( x , y ) (x, y) (x,y),有 h ( x ) ≤ d ( x , y ) + h ( y ) h(x) ≤ d(x, y) + h(y) h(x)≤d(x,y)+h(y).
理解: 此时相当于 J o h n s o n ′ s A l g o r i t h m Johnson's Algorithm Johnson′sAlgorithm,是对每条边重新分配权重 d ′ ( x , y ) = d ( x , y ) + h ( y ) − h ( x ) d'(x, y) = d(x, y) + h(y) - h(x) d′(x,y)=d(x,y)+h(y)−h(x)使之非负,然后使用 D i j k s t r a Dijkstra Dijkstra保证正确性 - Admissible(较弱)
对任意点有 h ( n ) < = h ∗ ( n ) h(n) <= h^{*}(n) h(n)<=h∗(n). h ∗ ( n ) h^{*}(n) h∗(n)是从 n n n到end的最优解。
反证:
假设存在该算法结果 f ( e n d ) > f ∗ = h ∗ ( s t a r t ) = 最 优 解 f(end) > f* = h^{*}(start) = 最优解 f(end)>f∗=h∗(start)=最优解,假设 n n n在最优路径上,由算法结束条件得 f ( n ) > f ( e n d ) f(n) > f(end) f(n)>f(end)。
f ( n ) = g ( n ) + h ( n ) = g ∗ ( n ) + h ( n ) ( n 在 最 优 路 径 上 ) < = g ∗ ( n ) + h ∗ ( n ) ( A d m i s s i b l e ) = f ∗ f ∗ > = f ( n ) > = f ( e n d ) ( c o n t r a d i c t i o n ) f(n) = g(n) + h(n) \\ = g^{*}(n) + h(n) (n在最优路径上) \\ <= g^{*}(n) + h^{*}(n) (Admissible)\\ = f^{*} \\ f^{*} >= f(n) >= f(end) (contradiction) f(n)=g(n)+h(n)=g∗(n)+h(n)(n在最优路径上)<=g∗(n)+h∗(n)(Admissible)=f∗f∗>=f(n)>=f(end)(contradiction)
注:if a node is reached by one path, removed from openSet, and subsequently reached by a cheaper path, it will be added to openSet again. This is essential to guarantee that the path returned is optimal if the heuristic function is admissible but not consistent. If the heuristic is consistent, when a node is removed from openSet the path to it is guaranteed to be optimal so the test ‘tentative_gScore < gScore[neighbor]’ will always fail if the node is reached again.
和例子中点4的情况相同