Chapter 9
■
From a to B with edsger and Friends
197
All Against All
In the next section, you’ll see a really cool algorithm for finding the shortest distances between
all pairs of nodes. It’s
a special-purpose algorithm that is effective even if the graph has a lot of edges. In this section, though, I’ll have a
quick look at a way to combine the two previous algorithms—Bellman-Ford and Dijkstra’s algorithm—into one that
really
shines in sparse graphs (that is, ones with relatively few edges). This is Johnson’s algorithm, one that seems to
be neglected in many courses and books on algorithm design, but which is really clever and which you get almost for
free, given what you already know.
The motivation for Johnson’s algorithm is the following: When solving the all-pairs shortest paths problem for
sparse graphs, simply using Dijkstra’s algorithm from every node is, in fact, a really good solution. That in itself doesn’t
exactly motivate a
new algorithm ... but the trouble is that Dijkstra’s algorithm doesn’t permit negative edges. For the
single-source shortest path problem, there isn’t much we can do about that, except use Bellman-Ford instead. For the
all-pairs problem, though, we can permit ourselves
some initial preprocessing to make all the weights positive.
The idea is to add a new node
s, with zero-weight edges to all existing nodes, and then to run Bellman-Ford from
s. This will give us a distance—let’s call it
h (
v)—from
s to each node
v in our graph. We can then use
h to adjust the
weight of every edge: We define the new weight as follows:
w ’(
u,
v) =
w(
u,
v) +
h(
u) -
h(
v). This definition has two very
useful properties. First, it guarantees us that every new weight
w’(
u,
v) is nonnegative (this
follows from the triangle
inequality, as discussed earlier in this chapter; see also Exercise 9-5). Second, we’re not messing up our problem! That
is, if we find the shortest paths with these
new weights, those paths will also be shortest paths (with other lengths,
though) with the
original weights. Now, why is that?
This is explained by a sweet idea called
telescoping sums: A sum like (
a -
b) + (
b -
c) + ... + (
y -
z) will collapse like
a telescope, giving us
a –
z. The reason is that every other summand is included once with a plus before it and once
with a minus, so they all sum to zero. The same thing happens to every path with the modified edges in Johnson’s
algorithm. For any edge (
u,
v)
in such a path, except for the first or last, the weight will be modified by adding
h(
u) and
subtracting
h (
v). The
next edge will have
v as its first node and will
add h(
v), removing it from the sum. Similarly, the
previous edge will have subtracted
h (
u), removing that.
The only two edges that are a bit different (in any path) are the first and the last. The first one isn’t a problem,
because
h (
s) will be zero, and
w (
s,
v) was set to zero for all nodes
v. But what about the last one? Not a problem. Yes,
we’ll
end up with h (
v) subtracted for the last node
v, but that will be true of
all paths ending at that node—the shortest
path will still be shortest.
The transformation doesn’t discard any information either, so once we’ve found the shortest paths using
Dijkstra’s algorithm, we can inversely transform all the path lengths. Using a similar telescoping argument, we can see
that we can get the real length of the shortest path from
u to
v by adding
h(
v) and subtracting
h(
u)
from our answer
based on the transformed weights. This gives us the algorithm implemented in Listing 9-4.
6
Do'stlaringiz bilan baham: