An Extremely Fast, Exact Algorithm for Finding Shortest

advertisement
U. Lauther
219
An Extremely Fast, Exact Algorithm for Finding
Shortest Paths in Static Networks with
Geographical Background
Ulrich Lauther
Siemens AG, München
ulrich.lauther@siemens.com
ABSTRACT
We present a new algorithm for fast and exact calculation of shortest paths in graphs
with geometrical information stored at the nodes (coordinates), e.g., road networks. The
method is based on pre-processing and therefore best suited for static graphs, i.e.,
graphs with fixed topology and edge costs.
In the pre-processing phase, the network is divided into regions and edge flags are calculated that indicate whether an edge belongs to a shortest path into a given region. In
the path calculation step, only those edges need to be investigated that carry the appropriate flag.
As compared to a classical Dijkstra implementation, we achieve an average speed-up of
a factor of 64 on a European truck driver’s roadmap containing about 500000 road segments. The concept of edge flags and how to use them in the path finding algorithm is
discussed in this paper; the pre-processing step is just outlined.
Results of both steps are presented for the above mentioned example.
INTRODUCTION
A huge amount of research work has been done concerning fast shortest
path algorithms, but still efficient exact algorithms are based on Dijkstra’s
algorithm (Dijkstra 1959), combined with efficient data structures for implementing the priority queue which is a central part of an efficient implementation.
Given a directed or undirected graph G (V, E) with nodeset V, |V| = n,
edgeset E, |E| = m, edges e (v, w), positive edge weights w(e), and two
special nodes s (source) and t (sink), calculating a shortest path (i.e., a path
with minimum total edge weight) between s and t can be solved efficiently
using Dijkstra’s algorithm. Depending on the data structures used and additional assumptions on the density of the graph and the distribution of edge
weights, the worst case complexity lies between O(n2) (for dense graphs)
220
U. Lauther
and O(m) (for a sparse graph with limited, integral edge weights (Dial et al.
1979, Tarjan 1983).
In a typical geographical application, e.g., finding a shortest path in a road
map, the expected runtime of a careful implementation is O(d2 log d) where
d is the number of edges in the shortest path; as we observe a circular expansion of the algorithm around the source node, O(d2) nodes will be touched and each one will be inserted into and retrieved from a priority queue
with size O(d2) once. (Updates of the priority queue are of the same order,
as the node degree in road networks is limited and small). "Careful
implementation" implies for instance that - other than usually seen in textbooks - an initialization phase processing all nodes in the network is
avoided.
Often, these runtimes are prohibitive, e.g., in autonomous automotive navigation systems with limited resources (memory and CPU power). In practice, some speed-up is often achieved by compromising accuracy: heuristics
are employed that limit the search space and hopefully find reasonable solutions. However, the latter is not guaranteed and not always achieved in
practice. Typical examples of such heuristics are the layer concept found in
autonomous car navigation systems and heuristic variants of the A * algorithm.
Much less effort has been spent on exact solutions which use preprocessing (Ertl 1996), i.e., trade off-line pre-processing time for runtime
in the target application. Of course, pre-processing is not trivial: we cannot
afford to calculate (and store!) all shortest paths that might be requested in
the application.
This paper describes a very successful pre-processing strategy and its efficient implementation. Here, successful means a big speed-up in the target
application and acceptable memory needs, efficient implies acceptable preprocessing time. Moreover, the resulting algorithms are exact, i.e., a shortest (cheapest) path is guaranteed to be found.
At least in our implementation, the suggested method does not only need an
abstract graph, but also some geometrical information: coordinates of
nodes. In addition, we assume positive length (or cost) of edges. No further
assumptions (e.g., planarity) are made.
BASIC IDEA: EDGE FLAGS
When we drive through a road network in real life, we usually do not calculate shortest paths at all; we follow signposts. These do not need to be very
U. Lauther
221
specific: driving from Munich to Hamburg, we do not (initially) care
whether we are heading for the Binnenalster or the Reeperbahn, we just
follow the signs to Hamburg.
This principle can be transferred to a software solution.
Let the road map be represented by an undirected graph G (V, E) with n
nodes and m edges. Edges carry two weights (e.g., length or estimated
travel time), one for the forward direction, and one for the backward direction. (A one-way street would have an infinite cost in one direction).
We first partition the whole network into r regions i. These regions should
be geometrically connected, but the only hard requirement is that each node
v of the network belongs to exactly one region, and that (in the application)
we have fast access to a node’s region (for instance by way of a region-id
per node). We then define two edge flags for each edge e (v, w) and region i, v_ flage,i , and w_flage,i . Let us (for now) assume that node v does
not belong to region i. Then edge flag v_ flage,i is set if there is a shortest
path from node v over edge e into region i. w_ flage,i is defined analogously. Thus we have m * r *2 different flags, and we need 1 bit to store
each of these flags. These flags are our road signs; how they can be calculated will be discussed later.
HOW TO USE EDGE FLAGS
Utilization of edge flags can be added to any existing shortest path algorithm. Fig. 1 shows the Dijkstra algorithm with edge flag enhancements
printed in bold face. We use pseudo-code, which is very near to the actual
implementation based on our C++ class library TURBO (Lauther 2000).
So we just need two additional lines, one to retrieve the region index of the
target node, the other one for skipping edges which cannot be on the shortest path to the target. This simple trick results in an enormous reduction of
nodes that need to be scanned, and thus yields a corresponding speed-up for
the shortest path calculation.
(Note that in a real application the initialization of all nodes would be done
outside the Dijkstra-procedure only once; inside the procedure, we would
keep track of touched nodes and only re-initialize these before the procedure returns.)
222
U. Lauther
int dijkstra(Graph& g, Node* source, Node* target) {
int target_region = target->region();
// initialize all nodes:
Node* v;
forall_nodes(v,g) v->dist = ;
// initialize priority queue:
priority_queue q;
source->dist = 0;
q.insert(source);
while ((v = q.del_min())) { // while q not empty
if (v == target) return v->dist;
Node* w;
Edge* e;
forall_adj_edges(w,v,e,g) { // scan node v
if (! flagged(v,e,target_region)) continue;
if (w->dist > v->dist + e->length) {
w->dist = v->dist + e->length;
if (q.member(w))
q.update(w);
else
q.insert(w);
}
}
}
return ;
}//dijkstra
Fig. 1:
Dijkstra´s Algorithm Extended by Edge Flags Checks
THE PROBLEM OF CONES
Coming back to our previous example: as we approach Hamburg, we need
to make up our mind, whether to head for Binnenalster or Reeperbahn. The
signs pointing to Hamburg are not very helpful anymore. When we use
edge flags as described above and animate the implementation, we observe
that in the initial part of the route very few nodes are touched that do not
belong to the final shortest path. This is due to the fact that usually only
one edge incident to a node v has its flag on for a far away region. When
we are near the region, the situation changes. As edge flags describe shortest routes to all nodes within the region, now many edges out of a node will
have its flag set and we observe a broad cone of touched nodes in front of
the target region.
This cone can easily be avoided: if we had started the shortest path calculation at the target, we would not have had a cone there, but one in front of
the source region. We can combine the two approaches (and their benefits)
by using a bidirectional shortest path algorithm, expanding from source and
target simultaneously. The two expansions will meet somewhere in the
U. Lauther
223
middle between source and target and hopefully before the cones start to
develop. This is at least true when source and target are far from each other; if they are close, we do not have a problem with computation time in
the first place.
Fig. 2:
Cone in Front of the Target Region
There is a small problem with this approach: when we expand from the target, we have to account for the fact that we are driving (one-way) roads in
the wrong direction. To compensate for this, we have to use the "wrong"
cost value for each edge traversed. However, the edge flags as defined
above have not been calculated for this situation. As a consequence, we
need four flags per bidirectional edge and region, two for the nominal driving direction and two for the wrong one.
We also have to make sure that the two paths developing from source and
target meet in a common node. To guarantee this, we have to make sure
that all edges (v,w) on a shortest path from v into region i are flagged, not
just one out of v (which would be sufficient in a unidirectional implementation).
A further complication arises, when we are inside the target region. This
will be discussed later.
224
U. Lauther
HOW TO CALCULATE EDGE FLAGS
The key question is, of course, how to calculate the edge flags in an efficient way. This is where the problem gets interesting (or where the fun begins). In this paper, we will only give a rough idea, without going into all
details.
A Simple but dead slow method
The simplest method is as follows: for each node v in a region I, we calculate a tree of shortest paths, with node v as its root. We can use Dijkstra’s
algorithm for this purpose. When a tree has been calculated, each node carries a distance label, which gives the node’s distance to the root. Then we
look at all edges e (v, w): if the difference between the two distances distance(v) and distance(w) corresponds to the length (or cost) of e, this edge
is in some shortest path out of region i and we can set the corresponding
edge flag. Flags from different trees within the same region are or-ed together. (Actually we are interested in shortest paths into region i, not out of
it; we fix this be using the "wrong" edge costs). Note that not only edges
which form the shortest-path tree are flagged, but all edges without "slack";
thus we have to inspect all edges after each tree calculation. After we have
done all these tree calculations (two for each node, as we need four flags,
see above) and edge inspections (after about 20 weeks for a road map of
Germany containing 1.2 million edges), we can write the collected flags to
disk and are done.
A somewhat faster method
We can classify the edges into two types: internal edges and interface
edges. Internal edges are those that connect two nodes which belong to the
same region. Interface edges connect nodes that belong to different (usually
geometrically adjacent) regions. Based on the classification of edges, we
can classify nodes into interior nodes and exported nodes. Exported nodes
are nodes which are incident to at least one interface edge. All others are
interior nodes. Obviously, when we try to reach a target node in some region i (the target region) from the outside, we have to traverse some interface edge and thus cross an exported node of that region. Therefore, it is
sufficient to apply the tree calculation process just discussed to exported
nodes. After nodes have been assigned to regions (we will see later how to
do this), it is easy to identify exported nodes and to store them in a list for
each region. We just look at all nodes of a region and check outgoing
edges; if the edge connects into another region, both nodes can be marked
as exported nodes and put into the respective list. If we now apply our flag
U. Lauther
225
calculation method to exported nodes only, we can cut down the runtime
for the mentioned data set from 20 weeks (estimated) to about 35 hours.
A fast but wrong method
One might be tempted to suggest the following clever way to achieve an
even faster solution: instead of expanding from one exported node of a region at a time, expand from all of them at the same time. This is easy to
implement: we initialize the priority queue of Dijkstra’s algorithm with all
the exported nodes, after setting their distance to zero. Then we keep expanding nodes until the queue is empty. This is equivalent to expanding
from some virtual inner node of the region.
This method is fast, but unfortunately wrong. Resulting flags do not reflect
shortest paths to any interior node of a region (or equivalently to any exported node), but to the nearest one. Thus, in the application, we would
generate paths that pass through the exported node of the target region that
is nearest to the source node and from there run along a shortest path to the
target node. This is not necessarily a shortest path between source and target.
The fast way
Another obvious attempt to achieve faster tree calculations uses the observation that trees rooted at neighbouring exported nodes will usually show
much similarity. A method that exploits this fact has been developed and
gives indeed another big speed-up. However, details of this procedure are
out of the scope of this paper.
Saving space
As described, we would need r*4 bits per edge when we use r regions.
Some of these flags are redundant (in case of one way streets). For some
edges all flags are zero and need not be stored individually. And different
edges may share the same set of flags. Using these observations, we can
greatly reduce the space needed for edge flags.
Run time and memory needs
Runtimes have been measured for pre-processing (tree calculation and setting edge flags) on a 1 GHz Pentium 4 running Linux. Input was a European truck road map containing 326159 nodes and 513567 edges (after
elimination of nodes with degree 2); the number of regions used is 139.
Tab. 1:
Runtime for Pre-processing for Various Methods
226
Mode
Expand from all nodes
Expand from exported nodes
The fast method
U. Lauther
Runtime
9 weeks
4.2 hours
12 minutes
Remarks
extrapolated
extrapolated
measured
The memory needed for storing edge flags is 6.9 bytes/edge.
Speed-up by parallel processing
If pre-processing needs to be really fast (e.g., to react to changing traffic
conditions), nearly linear speed-up can be achieved by parallel processing.
Different processors could work without any interaction on different regions. In a final merge phase, the independently calculated flags are
merged.
HOW TO USE EDGE FLAGS, A CLOSER LOOK
When discussing the flag-enhanced shortest path algorithms it was assumed
that the node v being scanned is outside the target region. Here we discuss
the general case. In the following, the term "target region" is used relative
to the mode of expansion in the bidirectional Dijkstra algorithm, i.e., if we
are expanding from the target node, the source node’s region is the relative
target region. There are two main cases to consider:
Case 1: Source and target node belong to different regions.
Sub case 1.1: Node v is outside the target region i. We follow edges out of
v only if the v_flag for region i has been set.
Sub case 1.2: Node v is inside the target region i. When investigating edge
e (v, w), there must be a shortest path from some exported node of region
i through v over edge e to node w; otherwise, this edge does not need to be
considered. We can check this by inspecting the edge flag of e for node w.
As we are now driving in the opposite direction relative to the exported
node passed earlier, we need to use flags for another direction than outside
the target region.
U. Lauther
227
Case 2: Source and target node belong to the same region.
Sub case 2.1: Node v is inside the target region i. Here we cannot use any
flags.
Sub case 2.2: Node v is outside the target region i. This is a total legal
situation; a shortest path between two nodes of one region may run through
one or more other regions. However, we know that we eventually need to
get back to the target region and we must do this along a shortest path. So
case 1.1 applies. Thus, even when source and target are within the same
region we achieve some speed-up, as we limit the investigation of nodes
which are outside this region.
Let us reconsider case 1.1: a shortest path from node v over edge e to the
target node must exist; the respective flag is checked. But, there must also
be a shortest path from node w over edge e back to the source region (driving in the wrong direction); otherwise, w would be reached along a shortest
path over another edge. We can use this observation to construct a unidirectional algorithm that avoids the cones problem discussed earlier; however,
the bidirectional version is slightly faster.
For this method to work, it is essential to flag all tight edges, not just those
in the shortest path tree; this is the case in our implementation.
HOW TO DEFINE REGIONS
So far we have not considered how regions are defined. This may be application specific. Many applications anyhow use - due to main memory shortage - a parcelized data structure; these parcels or groups of parcels could
be used to define regions.
In our prototype implementation, there are two different methods implemented for region definition, a very simple rectangular grid method and a
slightly more sophisticated square covering method. Both methods take an
approximate number of regions, r, as input parameter.
Grid based region definition
We first calculate the bounding box of the whole network; this takes one
pass over all nodes. From the area of the bounding box and the number r of
regions to be generated, we calculate the area and the width w of one square
region. Relating w to width and height of the bounding box gives us two
numbers nx and ny. Now, the bounding box is cut into nx columns and nx
rows, resulting in nearly square regions, whose addresses are stored in a
two-dimensional array. In a second pass, nodes are assigned to regions;
228
U. Lauther
horizontal and vertical region indices can be calculated from a node’s coordinates and the final grid width and height derived from nx and ny. Finally,
empty regions are weeded out. In general, the number of regions created
will be smaller than the initially requested number.
Square covering
Here again, we start by finding the bounding box and the width of a square
region. Based on this width, we imagine the whole area to be partitioned
into horizontal stripes and create one empty list of nodes for each stripe.
Now, each node is inserted into the appropriate list based on its ycoordinate. Lists are then sorted by x-coordinates. Scanning the sorted lists,
we can assign nodes to regions; whenever the next node does not fit into
the current region, a new region is started. Other than in the grid-based
method, gaps develop between horizontally consecutive regions and regions are in general not vertically aligned. The number of regions (of limited size) needed to cover the whole set of nodes is smaller than in the grid
based method.
We can further reduce this number when we maintain the lowest xcoordinate for each region while it is filled and then extend the region vertically within its width limit, stealing nodes from the next stripe. This will
lead to geometrically overlapping rectangular regions, but of course each
node belongs to exactly one region.
A similar algorithm (Gonzales 1991) is well known for covering a set of
points with circular disks of a specified maximum size.
Speed-up of the Shortest-Path Calculations
To measure the speed-up achieved by our pre-processing technique (which
is larger for long paths) in a fair way, we carried out the following experiment.
Out of the 326159 nodes in our roadmap, we picked two nodes at random
and did the shortest path calculation between these two nodes. This process
was repeated 300 times, once with pre-processing and once using the plain
vanilla Dijkstra algorithm, using an efficient priority queue implementation, the radix queue (Ahuja et al. 1993). The results are shown in Table 2.
U. Lauther
229
Tab. 2:Comparison of Runtimes with and without Pre-processing
Nodes
Edges
Regions
Runtime without pre-processing [sec]
Runtime with pre-processing [sec]
Speed-up
326159
513567
139
90.7
1.41
64.3
Note that the average speed-up is about 64; it is much larger for long paths.
Application Scenarios
There are two main applications for our algorithm in the context of route
planning:
Firstly, in autonomous car navigation systems, the pre-processing can be
done once and results are stored on the systems CDROM. This way, we can
immensely speed up route calculations (including slow seeks on the
CDROM).
However, when we want to be able to react to traffic jams, we have to fall
back to heuristic solutions.
Secondly, in a navigation system with client server architecture (a central
route planning server with the cars as clients), we can do the pre-processing
at the server in a cyclic fashion (say, every 15 minutes) based on the current traffic situation. The server can then process a large number of requests from the clients per time unit.
Availability
The concept of edge flags and the associated algorithms are protected by a
patent. Source code licenses are available.
SUMMARY
A new, pre-processing based algorithm for the calculation of shortest paths
on road and similar networks has been presented. The method uses moderate pre-processing time and generates only a small amount of additional
information per edge. It guarantees an exact solution of the shortest path
problem and achieves an average speed-up of 64 on a typical European
road map.
230
U. Lauther
REFERENCES
R.K. Ahuja, T.L. Magnanti, and J.B. Orlin,"Network Flows", Prentice Hall,
Englewood Cliffs, New Jersey 07632, 1993
R. Dial, F.Glover, D.Karney and D. Klingman, "A computational analysis
of alternative algorithms for finding shortest path trees." Networks,
9(1979), pp. 215-248
E. W. Dijkstra, "A note on two problems in connection with graphs", Numer. Math., 1 (1959), pp. 269-271
G. Ertl, "Shortest Path Calculation in Large Road Networks", SFB-Report
69, May 1996, http://jupiter.kaist.ac.kr/ jhoon/replist6.html
T. F. Gonzales, "Covering a Set of Points in Multidimensional Space", Inf.
Proc. Letters 40 (1991), pp. 181 -188
U. Lauther, “The C++ Class Library TURBO – a Toolbox for Discrete Optimization, software@siemens, Vol. 7, pp. 34-36
R. E. Tarjan, "Data Structures and Network Algorithms, SIAM, 1983
Download