Shortest Paths
Given a directed graph G
with weights on the edges, we define the shortest path between two vertices i
and j
as a path with the minimum total weight. The all-pairs of shortest paths problem is to find the shortest path between every pair of vertices in the graph.

We use dynamic programming to solve this problem. The first step is to define a subproblem, after that derive a recursive formula. Using this formula, we can compute the value of the subproblem. Finally, we construct the optimal solution from the computed values.
Subproblem
Let G
be the input graph. We name the vertices in G
by numbers, i.e. V = [0, 1, ..., n-1]
. Consider any two vertices i
and j
in V
, let c[i, j]
be the length of edge (i, j)
. Note that if there is no edge from i
to j
, then we set c[i, j]
to infinity. Thus, we may assume w.l.o.g. that G
is a complete graph.
Let d(i, j, k)
be the shortest path length between i
and j
, whose intermediate vertices of the shortest path are drawn from [1, ..., k-1]
. The subproblem is to compute d(i, j, k)
, given i
, j
and k
.
Note that d(i, j, n)
returns the shortest path length between i
and j
.
Recursive Formula
Let P
be the shortest path w.r.t. d(i, j, k)
. Thus its intermediate vertices must be drawn from [0, ..., k-1]
. We consider the last vertex from the set, i.e. k-1
. There are two cases.
- Vertex
k-1
is not in the intermediate vertices ofP
. In this case,d(i, j, k) = d(i, j, k-1)
. - Vertex
k-1
is in the intermediate vertices ofP
. We can splitP
into two paths:P_1 = (i, ..., k-1)
andP_2 = (k-1, ..., j)
. Note thatP_1
andP_2
are shortest paths, sinceP
is the shortest path. Also,k-1
is the last vertex inP_1
and the first vertex inP_2
. Thus, we have $$ d(i, j, k) = d(i, k-1, k-1) + d(k-1, j, k-1) $$
Summarizing, we have the following recursive formula:
$$ d(i, j, k) = \min\set{d(i, j, k-1),~ d(i, k-1, k-1) + d(k-1, j, k-1)} $$
Initial Conditions
If k = 0
, it implies there is no intermediate vertices between i
and j
. In this case, the shortest path is the edge (i, j)
.
$$ d(i, j, 0) = c[i, j], \quad \forall i, j \in V $$
Solution
To construct the shortest paths, we use a table p(i, j, k)
to store the shortest paths between i
and j
whose intermediate vertices are drawn from [0, ..., k-1]
.
Initially, p(i, j, 0)
is an empty list []
for all i, j
.
Similar to the recursive formula, we have the following recursive formula for p(i, j, k)
:
- If
d(i, j, k) = d(i, j, k-1)
, thenp(i, j, k) = p(i, j, k-1)
. - If
d(i, j, k) = d(i, k-1, k-1) + d(k-1, j, k-1)
, thenp(i, j, k) = p(i, k-1, k-1) + [k-1] + p(k-1, j, k-1)
.
Code
The dynamic programming algorithm for all pairs of shortest paths can implemented as below.
import numpy as np
import copy
def shortest_paths(c):
""" All-pairs of shortest paths
:param c: cost matrix
:return: distance matrix and shortest paths
"""
n = len(c)
# Initialize length of shortest paths
d = np.full((n, n, n+1), np.inf)
d[:, :, 0] = copy.copy(c)
# Initialize shortest paths
p = np.empty((n, n, n+1), dtype=object)
for i in range(n):
for j in range(n):
# The list contains the intermediate vertices in the shortest path
p[i][j][0] = []
for k in range(1, n+1):
for i in range(n):
for j in range(n):
if d[i][j][k-1] > d[i][k-1][k-1] + d[k-1][j][k-1]:
d[i][j][k] = d[i][k-1][k-1] + d[k-1][j][k-1]
p[i][j][k] = p[i][k-1][k-1] + [k-1] + p[k-1][j][k-1]
else:
d[i][j][k] = d[i][j][k-1]
p[i][j][k] = p[i][j][k-1]
return d[:, :, n], p[:, :, n]
To see the result, we may define a function print_paths
to print the shortest paths and their lengths.
def print_paths(p, c):
"""
:param p: shortest paths
:param c: cost matrix
"""
n = len(c)
for i in range(n):
for j in range(n):
if i == j:
continue
path = [i] + p[i][j] + [j]
path_length = sum(c[path[k]][path[k+1]] for k in range(len(path)-1))
print(f"({i}, {j}): path = {path}, length = {path_length}")
In addition, we define a function random_instance
to generate a random instance of the problem.
def random_instance(n):
"""
:param n: number of nodes
:return: random cost matrix
"""
import numpy as np
c = np.random.randint(1, 1000, (n, n))
for i in range(n):
c[i][i] = 0
return c
if __name__ == '__main__':
c = random_instance(10)
p, _ = shortest_paths(c)
print_paths(p, c)