Relax ALL the Edges: Bellman-Ford
Get to know the Bellman-Ford algorithm and its applications in solving the shortest paths problem.
We'll cover the following...
Understanding of Bellman-Ford algorithm
The simplest implementation of Ford’s generic shortest-path algorithm was first sketched by Alfonso Shimbel in 1954, described in more detail by Edward Moore in 1957, and independently rediscovered by Max Woodbury and George Dantzig in 1957, by Richard Bellman in 1958, and by George Minty in 1958. (Neither Woodbury and Dantzig nor Minty published their algorithms.) In full compliance with Stigler’s Law, the algorithm is almost universally known as Bellman-Ford, because Bellman explicitly used Ford’s 1956 formulation of relaxing edges, although some authors refer to “Bellman-Kalaba” and a few early sources refer to “Bellman-Shimbel.”
Below is a complete run of Dijkstra’s algorithm on a graph with negative edges. At each iteration, bold edges indicate predecessors; shaded vertices are in the priority queue; and the bold vertex is the next to be scanned.
The Shimbel/Moore/Woodbury-Dantzig/Bellman-Ford/Kalaba/Minty/Brosh algorithm can be summarized in one line:
- Bellman-Ford: Relax all the tense edges, then recurse.
Algorithm
Implementation
import sys# Graph data structureMAX_V = 1000 # Maximum number of verticesadj = [[] for _ in range(MAX_V)] # Adjacency list of edges# Bellman-Ford algorithmdist = [sys.maxsize] * MAX_V # Shortest distance from source vertextense = [False] * MAX_V # Whether a vertex has been updated during the current iterationdef InitSSSP(s):# Initialize shortest path distancesfor i in range(MAX_V):dist[i] = sys.maxsizedist[s] = 0tense[s] = Truedef HasTenseEdge():# Check if there is at least one tense edgefor u in range(MAX_V):if tense[u]:return Truereturn Falsedef Relax(u, v, weight):# Relax the edge (u, v)if dist[u] != sys.maxsize and dist[v] > dist[u] + weight:dist[v] = dist[u] + weighttense[v] = Truedef BellmanFord(s):# Initialize single source shortest pathInitSSSP(s)# Loop until no more tense edges existwhile HasTenseEdge():# Iterate through all edgesfor u in range(MAX_V):for i in range(len(adj[u])):v = adj[u][i][0]weight = adj[u][i][1]# Relax the edge if it is tenseif tense[u] and tense[v]:Relax(u, v, weight)tense[u] = Falseif __name__ == '__main__':# Build example graphadj[0].append((1, 5))adj[1].append((2, 2))adj[2].append((3, 3))adj[3].append((4, -4))adj[4].append((1, 1))# Run Bellman-Ford algorithm from source vertex 0BellmanFord(0)# Print shortest distances from source vertexfor i in range(MAX_V):print(f"Shortest distance from vertex 0 to {i} is {dist[i]}")
Explanation
-
Line 5: We create an adjacency list to store the edges in the graph. The list is initialized as an empty list for each vertex.
-
Line 8: We initialize the distances to each vertex with the maximum value of an integer.
-
Line 11: This function initializes the distances and tense lists for the source vertex
s
. -
Line 33: This function implements the Bellman-Ford algorithm by initializing the distances and tense lists for the source vertex and then iterating through all edges until no more tense edges exist. For each edge, it checks if it is tense and relaxes it if it can be improved. After each iteration, it marks all the vertices as not tense.
-
Lines 63–64: We iterate through all vertices and print the shortest distance from vertex
0
to each vertex.
The following lemma is the key to proving both the correctness and efficiency of Bellman-Ford. For every vertex and non-negative integer , let denote the length of the shortest walk in from to consisting of at most edges. In particular, and for all .
Lemma: For every vertex and non-negative integer , after iterations of the main loop of Bellman-Ford, we have .
Proof: The proof proceeds by induction on . The base case is trivial, so assume . Fix a vertex , and let be the shortest walk from to consisting of at most edges (breaking ties arbitrarily). By definition, has length . There are two cases to consider.
- Suppose has no edges. Then must be the trivial walk from to , so and . We set in , and since can never increase, we always have .
- Otherwise, let be the last edge of . The induction hypothesis implies that after iterations,