Bellman-Ford and Negative Cycles
Bellman-Ford finds shortest paths even with negative edge weights, and as a side effect it detects negative cycles — which is exactly how currency arbitrage gets found algorithmically.
Prerequisites: Dijkstra's Shortest Paths, Graph Representations
Dijkstra's algorithm finalises each node's distance the moment it's popped from the queue, and that shortcut breaks the instant an edge has negative weight — a later, cheaper path through a negative edge can undercut a distance that was already declared final. Bellman-Ford gives up that shortcut and, in exchange, handles negative edges correctly, plus it can tell you when the whole question "what's the shortest path" stops making sense.
The idea: relax every edge, V-1 times, no shortcuts
Bellman-Ford does the same relaxation Dijkstra does —
— but instead of picking edges cleverly via a priority queue, it just relaxes every edge in the graph, in any order, and repeats the whole pass times. The claim is: after passes, every shortest path using at most edges has been found. Since a shortest simple path visits each node at most once, it uses at most edges, so passes are guaranteed enough — no priority queue, no greedy assumption, so negative weights are fine. Cost is , worse than Dijkstra's , which is the price of dropping the non-negativity requirement.
The bonus: run one more pass, pass . If any distance still improves, some cycle in the graph has negative total weight — a loop you could traverse forever to make the "shortest path" arbitrarily negative, so no finite answer exists. That -th pass is a negative-cycle detector.
Worked example: currency arbitrage
Suppose $1 buys 0.90 EUR, 1 EUR buys 0.80 GBP, and 1 GBP buys 1.45 USD. Multiply the three rates: . Round-tripping $1 through EUR and GBP returns $1.044 — free money, an arbitrage. Bellman-Ford finds this automatically by weighting each edge : since turns products into sums, a profitable loop (product ) becomes a cycle whose weights sum to a negative number, and the -th relaxation pass will keep finding improvements on it forever, flagging exactly that cycle.
def bellman_ford(edges, n, src): # edges: list of (u, v, w); O(VE)
dist = [float("inf")] * n
dist[src] = 0
for _ in range(n - 1):
for u, v, w in edges:
if dist[u] + w < dist[v]:
dist[v] = dist[u] + w
for u, v, w in edges: # V-th pass: negative-cycle check
if dist[u] + w < dist[v]:
return None # negative cycle reachable from src
return dist
Bellman-Ford relaxes every edge times instead of using a priority queue — slower than Dijkstra but correct with negative weights, and its extra pass detects negative cycles. Weighting FX edges as turns arbitrage-hunting into negative-cycle detection.
A negative cycle means "no shortest path exists," not "the shortest path is very negative." If your traversal can loop through it, the true minimum is , and Bellman-Ford's job on such a graph is only to report that the cycle exists, not to output a distance.
Where it shows up
Interview framing: "cheapest flights within k stops" bounds the number of relaxation passes to , which is Bellman-Ford with an early stop. On a desk, triangular and multi-leg FX arbitrage detection is the canonical application; the same negative-cycle idea generalises to any market where implied round-trip pricing (funding rates, cross-listed securities, calendar spreads) should multiply to 1 but occasionally doesn't.
Practice in interviews
Further reading
- Cormen, Leiserson, Rivest & Stein, Introduction to Algorithms (ch. 24)
- Bellman, On a Routing Problem (1958)