Lifelong Planning A*
LPA* or Lifelong Planning A* is an incremental heuristic search algorithm based on A*. It was first described by Sven Koenig and Maxim Likhachev in 2001.[1] DescriptionLPA* is an incremental version of A*, which can adapt to changes in the graph without recalculating the entire graph, by updating the g-values (distance from start) from the previous search during the current search to correct them when necessary. Like A*, LPA* uses a heuristic, which is a lower boundary for the cost of the path from a given node to the goal. A heuristic is admissible if it is guaranteed to be non-negative (zero being admissible) and never greater than the cost of the cheapest path to the goal. Predecessors and successorsWith the exception of the start and goal node, each node n has predecessors and successors:
In the following description, these two terms refer only to the immediate predecessors and successors, not to predecessors of predecessors or successors of successors. Start distance estimatesLPA* maintains two estimates of the start distance g*(n) for each node:
For the start node, the following always holds true: If rhs(n) equals g(n), then n is called locally consistent. If all nodes are locally consistent, then a shortest path can be determined as with A*. However, when edge costs change, local consistency needs to be re-established only for those nodes which are relevant for the route. Priority queueWhen a node becomes locally inconsistent (because the cost of its predecessor or the edge linking it to a predecessor has changed), it is placed in a priority queue for re-evaluation. LPA* uses a two-dimensional key: Entries are ordered by k1 (which corresponds directly to the f-values used in A*), then by k2. Node expansionThe top node in the queue is expanded as follows:
Since changing the g-value of a node may also change the rhs-values of its successors (and thus their local consistence), they are evaluated and their queue membership and key is updated if necessary. Expansion of nodes continues with the next node at the top of the queue until two conditions are met:
Initial runThe graph is initialized by setting the rhs-value of the start node to 0 and its g-value to infinity. For all other nodes, both the g-value and the rhs-value are assumed to be infinity until assigned otherwise. This initially makes the start node the only locally inconsistent node, and thus the only node in the queue. After that, node expansion begins. The first run of LPA* thus behaves in the same manner as A*, expanding the same nodes in the same order. Cost changesWhen the cost of an edge changes, LPA* examines all nodes affected by the change, i.e. all nodes at which one of the changed edges terminates (if an edge can be traversed in both directions and the change affects both directions, both nodes connected by the edge are examined):
After that, node expansion resumes until the end condition has been reached. Finding the shortest pathOnce node expansion has finished (i.e. the exit conditions are met), the shortest path is evaluated. If the cost for the goal equals infinity, there is no finite-cost path from start to goal. Otherwise, the shortest path can be determined by moving backwards:
PseudocodeThis code assumes a priority queue
void main() {
initialize();
while (true) {
computeShortestPath();
while (!hasCostChanges())
sleep;
for (edge : getChangedEdges()) {
edge.setCost(getNewCost(edge));
updateNode(edge.endNode);
}
}
}
void initialize() {
queue = new PriorityQueue();
for (node : getAllNodes()) {
node.g = INFINITY;
node.rhs = INFINITY;
}
start.rhs = 0;
queue.insert(start, calculateKey(start));
}
/** Expands the nodes in the priority queue. */
void computeShortestPath() {
while ((queue.getTopKey() < calculateKey(goal)) || (goal.rhs != goal.g)) {
node = queue.pop();
if (node.g > node.rhs) {
node.g = node.rhs;
} else {
node.g = INFINITY;
updateNode(node);
}
for (successor : node.getSuccessors())
updateNode(successor);
}
}
/** Recalculates rhs for a node and removes it from the queue.
* If the node has become locally inconsistent, it is (re-)inserted into the queue with its new key. */
void updateNode(node) {
if (node != start) {
node.rhs = INFINITY;
for (predecessor: node.getPredecessors())
node.rhs = min(node.rhs, predecessor.g + predecessor.getCostTo(node));
if (queue.contains(node))
queue.remove(node);
if (node.g != node.rhs)
queue.insert(node, calculateKey(node));
}
}
int[] calculateKey(node) {
return {min(node.g, node.rhs) + node.getHeuristic(goal), min(node.g, node.rhs)};
}
PropertiesBeing algorithmically similar to A*, LPA* shares many of its properties.
For an A* implementation which breaks ties between two nodes with equal f-values in favor of the node with the smaller g-value (which is not well-defined in A*), the following statements are also true:
LPA* additionally has the following properties:
VariantsReferences
|
Portal di Ensiklopedia Dunia