SHORTEST PATHS IN GRAPHS Tuesday April 5, 2016 SHORTEST PATHS IN GRAPHS CS16: Introduction to Data Structures & Algorithms
Outline Shortest Paths Breadth First Search Dijkstra’s Algorithm Tuesday April 5, 2016 Outline Shortest Paths Breadth First Search Dijkstra’s Algorithm Bellman-Ford Algorithm
Tuesday April 5, 2016 What is a shortest path? The problem: Given a graph of nodes and weighted edges, what is the shortest path from one node to another? Why might we want to do this? Many problems can be represented by these sorts of graphs! Nodes can represent possible states, and the edges between them represent the cost to get from one state to another (distance, price, etc…)
Applications We use shortest path algorithms quite frequently Tuesday April 5, 2016 Applications We use shortest path algorithms quite frequently Walking from one physical address to another Finding something on the web with the fewest number of clicks (navigating to a virtual address) Shortest paths are calculated for us, too Routing phone call or network packets from your device to the recipient’s device Google Maps and other GPS applications
Simpler Problem: Unit Edges Tuesday April 5, 2016 Simpler Problem: Unit Edges Let’s start with a simpler problem A graph with unit edge weights means every edge has a weight or cost of 1, and therefore every edge is equal A B C D E 1
Simpler Problem: Unit Edges (2) Tuesday April 5, 2016 Simpler Problem: Unit Edges (2) Starting at A, what’s the shortest path to each node? B: [A, B] D: [A, B, D] or [A, C, D] C: [A, C] E: [A, B, E] or [A, C, E] A B C D E 1
Simpler Problem: Unit Edges (3) Tuesday April 5, 2016 Simpler Problem: Unit Edges (3) Is there an algorithm we’ve already learned to solve this problem? Hint: yes! What graph traversals have we learned? A B C D E 1
Breadth First Search A B C D E Tuesday April 5, 2016 Breadth First Search Breadth first search always reaches the goal node in the fewest number of steps! Let’s look at the path from A to E… A B C D E 1
Breadth First Search Simulation Tuesday April 5, 2016 Breadth First Search Simulation Recall that breadth first search utilizes a queue of nodes to visit Start by enqueuing the start node, and decorate nodes with previous pointers to keep track of the path A B C D E 1 Queue A
Breadth First Search Simulation Tuesday April 5, 2016 Breadth First Search Simulation Dequeue A from the queue, decorate its neighbors with the previous node “A” and enqueue them prev: A Queue B C A B C D E 1 prev: A
Breadth First Search Simulation Tuesday April 5, 2016 Breadth First Search Simulation Dequeue B, and repeat, ignoring neighbors that have already been decorated Queue C D E prev: A prev: B A B C D E 1 prev: A prev: B
Breadth First Search Simulation Tuesday April 5, 2016 Breadth First Search Simulation Dequeuing C and D has no effect, since their neighbors have all been decorated Queue E prev: A prev: B A B C D E 1 prev: A prev: B
Breadth First Search Simulation Tuesday April 5, 2016 Breadth First Search Simulation When we finally dequeue E, we can simply traverse the prev pointers to return the optimal path [A, B, E] prev: A prev: B A B C D E 1 prev: A prev: B
Non-unit Edge Weights But what if the edge weights are not all 1? Tuesday April 5, 2016 Non-unit Edge Weights But what if the edge weights are not all 1? Things become more complicated! A B C D E 4 2 3 1 5
Non-unit Edge Weights (2) Tuesday April 5, 2016 Non-unit Edge Weights (2) Goal Node Shortest Path Shortest Distance B [A, C, B] 3 C [A, C] 2 D [A, C, B, D] 5 E [A, C, B, E] 6 A B C D E 4 2 3 1 5
Shortest Path Application – Road Trip! Tuesday April 5, 2016 Shortest Path Application – Road Trip! Pat and Sarah want to get from Providence to San Francisco following a (very) limited set of interstate highways We represent the cities as nodes and the edges as the highways Our goal is to get to SF following the shortest possible path
Our Graph Start 10 PVD 10 CLE CHI 15 10 35 NYC End 5 20 SF PHL 20 15 Tuesday April 5, 2016 Our Graph Start 10 PVD 10 CLE CHI 15 10 35 NYC End 5 20 SF PHL 20 15 STL 15 10 DC 10 10 10 15 LA PHX ATL 10 20
One Possible Path What is the cost of this path? Start 10 PVD 10 CLE Tuesday April 5, 2016 One Possible Path Start 10 PVD 10 CLE CHI 15 10 35 NYC End 5 20 SF PHL 20 15 STL 15 10 DC 10 10 10 15 LA PHX ATL 10 20 What is the cost of this path?
Tuesday April 5, 2016 One Possible Path Start 10 PVD 10 CLE CHI 15 10 35 NYC End 5 20 SF PHL 20 15 STL 15 10 DC 10 10 10 15 LA PHX ATL 10 20 What is the cost of this path? 55 … is there a shorter path?
The Shortest Path Shorter path: 50 Start 10 PVD 10 CLE CHI 15 10 35 Tuesday April 5, 2016 The Shortest Path Start 10 PVD 10 CLE CHI 15 10 35 NYC End 5 20 SF PHL 20 15 STL 15 10 DC 10 10 10 15 LA PHX ATL 10 20 Shorter path: 50
Finding the Shortest Path Tuesday April 5, 2016 Finding the Shortest Path Why does BFS work when all edges have the same weight? Nodes are visited in order of their total distance from the start We need a way to visit nodes in order of their distance from the start node even when the edges have distinct weights! How can we do this? Hint: once again, we’ll use a data structure we’ve already introduced!
Finding the Shortest Path (2) Tuesday April 5, 2016 Finding the Shortest Path (2) Use a priority queue! The priorities stored are the total distances from the start to the node By visiting nodes in the order returned by removeMin(), you visit the nodes in order of how far they are from the start! Because you don’t explore a node until all of the ones closer to the start have already been explored, you ensure that you have found the shortest path to that node
Dijkstra’s Algorithm The algorithm is as follows: Tuesday April 5, 2016 Dijkstra’s Algorithm The algorithm is as follows: Decorate all nodes with distance ∞ except for the start node, which has distance 0 Add all nodes to a priority queue, with their distance decorations as the priority While the priority queue isn’t empty: Remove the node from the queue with minimal priority. Update the distances to the removed node’s neighbors if the distances have decreased When the algorithm terminates, every node has been decorated with the minimal cost to the node from the start
Dijkstra’s Algorithm Example Tuesday April 5, 2016 Dijkstra’s Algorithm Example S B A D C 7 8 2 3 1 5 4 ∞ Step 1: Label the starting vertex with distance zero and the other vertices with distance infinity. Add all the nodes to the priority queue. S B A D C 7 8 2 3 1 5 4 ∞ Step 2: Remove from the priority queue the node with minimal priority (S in this example). Calculate the distance from the start to the removed vertex’s neighbors by adding the adjacent edge weights to S’s distance decoration.
Dijkstra’s Algorithm Example Tuesday April 5, 2016 Dijkstra’s Algorithm Example S B A D C 7 8 2 3 1 5 4 10 Step 3: Repeat the previous step, while the priority queue isn’t empty, removing A this time. The priorities of nodes in the priority queue may have to be updated, like B’s priority in this example. S B A D C 7 8 2 3 1 5 4 6 Step 4: Repeat again by removing vertex B. As always, update any distances that are shorter using this path than before (for example, C now has a distance of 6, not 10).
Dijkstra’s Algorithm Example Tuesday April 5, 2016 Dijkstra’s Algorithm Example S B A D C 7 8 2 3 1 5 4 6 Step 5: Repeat, this time removing C. S B A D C 7 8 2 3 1 5 4 6 Step 6: After removing D, every node has been visited and thus every node has been decorated with the shortest distance to the node from the start.
Tuesday April 5, 2016 Dijkstra’s Algorithm Note that the previous example just decorated each node with its shortest distance but did not create paths How could you enhance the algorithm to return the shortest path to a particular node? Previous pointers! Let’s do another example, but this time without explanation – try to explain what the algorithm is doing at each step
Dijkstra’s Example A B C D E 4 2 3 1 5 A B C D E ∞ Tuesday April 5, 2016 Dijkstra’s Example A B C D E 4 2 3 1 5 A B C D E ∞
Dijkstra’s Example A B C D E 4 2 3 1 5 A B C D E 4 2 ∞ Tuesday April 5, 2016 Dijkstra’s Example A B C D E 4 2 3 1 5 A B C D E 4 2 ∞
Dijkstra’s Example (2) A B C D E 4 2 3 1 5 A B C D E 3 2 6 7 Tuesday April 5, 2016 Dijkstra’s Example (2) A B C D E 4 2 3 1 5 A B C D E 3 2 6 7
Dijkstra’s Example (3) A B C D E 4 2 3 1 5 A B C D E 3 2 5 6 Tuesday April 5, 2016 Dijkstra’s Example (3) A B C D E 4 2 3 1 5 A B C D E 3 2 5 6
Dijkstra’s Example (4) A B C D E 4 2 3 1 5 A B C D E 3 2 5 6 Tuesday April 5, 2016 Dijkstra’s Example (4) A B C D E 4 2 3 1 5 A B C D E 3 2 5 6
Tuesday April 5, 2016 Dijkstra’s Algorithm Dijkstra’s algorithm is an example of a particular class of algorithms we touched on earlier in the semester Since the algorithm uses a priority queue, at each step of iteration, we consider the next closest node given the information we have What algorithm paradigm does this fall under? Dijkstra’s is a greedy algorithm
Dijkstra’s Algorithm Pseudocode Tuesday April 5, 2016 Dijkstra’s Algorithm Pseudocode function dijkstra(G, s): // Input: A graph G with vertices V, and a start vertex s // Output: Nothing // Purpose: Decorate nodes with shortest distance from s for v in V: v.dist = infinity // Initialize distance decorations v.prev = null // Initialize previous pointers to null s.dist = 0 // Set distance to start to 0 PQ = PriorityQueue(V) // Use v.dist as priorities while PQ not empty: u = PQ.removeMin() for all edges (u, v): //each edge coming out of u if v.dist > u.dist + cost(u, v): // cost() is weight v.dist = u.dist + cost(u,v) // Replace as necessary v.prev = u // Maintain pointers for path PQ.replaceKey(v, v.dist)
Dijkstra’s Algorithm Runtime Tuesday April 5, 2016 Dijkstra’s Algorithm Runtime function dijkstra(G, s): // Input: A graph G with vertices V, and a start vertex s // Output: Nothing // Purpose: Decorate nodes with shortest distance from s for v in V: // O(|V|) v.dist = infinity v.prev = null s.dist = 0 PQ = PriorityQueue(V) while PQ not empty: // O(|V|) u = PQ.removeMin() // Depends on PQ implementation! for all edges (u, v): // O(|E|) if v.dist > u.dist + cost(u, v): v.dist = u.dist + cost(u,v) v.prev = u PQ.replaceKey(v, v.dist) // Depends on PQ implementation!
Dijkstra’s Runtime Analysis Tuesday April 5, 2016 Dijkstra’s Runtime Analysis Depends on the Priority Queue’s implementation! Array or Linked List removeMin() takes O(|V|) time since you have to iterate through to find the minimum element replaceKey() is O(1) since you already have the node when you change its key Runtime here is 𝑂(|𝑉|2 + |𝐸|) = 𝑂(|𝑉|2 ), since |E| <= |V|2 Binary Heap (like you did in the Java project) removeMin() takes 𝑂(𝑙𝑜𝑔|𝑉|) replaceKey() takes 𝑂(𝑙𝑜𝑔|𝑉|), since you may need to downheap Total runtime is 𝑂( |V| 𝑙𝑜𝑔 |𝑉| + |E| 𝑙𝑜𝑔 |𝑉|) = 𝑂( (|𝑉|+|𝐸|) 𝑙𝑜𝑔 |𝑉| ) |E| <= |V|^2
Dijkstra’s Runtime – Array/Linked List Tuesday April 5, 2016 Dijkstra’s Runtime – Array/Linked List function dijkstra(G, s): // Input: A graph G with vertices V, and a start vertex s // Output: Nothing // Purpose: Decorate nodes with shortest distance from s for v in V: // O(|V|) v.dist = infinity v.prev = null s.dist = 0 PQ = PriorityQueue(V) while PQ not empty: // O(|V|) u = PQ.removeMin() // O(|V|) for all edges (u, v): // O(|E|) if v.dist > u.dist + cost(u, v): v.dist = u.dist + cost(u,v) v.prev = u PQ.replaceKey(v, v.dist) // O(1)
Dijkstra’s Runtime – Heap Tuesday April 5, 2016 Dijkstra’s Runtime – Heap function dijkstra(G, s): // Input: A graph G with vertices V, and a start vertex s // Output: Nothing // Purpose: Decorate nodes with shortest distance from s for v in V: // O(|V|) v.dist = infinity v.prev = null s.dist = 0 PQ = PriorityQueue(V) while PQ not empty: // O(|V|) u = PQ.removeMin() // O(log(|V|)) for all edges (u, v): // O(|E|) if v.dist > u.dist + cost(u, v): v.dist = u.dist + cost(u,v) v.prev = u PQ.replaceKey(v, v.dist) // O(log(|V|))
Dijkstra’s Runtime - Heap Tuesday April 5, 2016 Dijkstra’s Runtime - Heap What’s the runtime? O(|V|)+O(|V|)*O(log|V|)+O(|E|)*O(log|V|) → 𝑂( (|𝑉|+|𝐸|) 𝑙𝑜𝑔 |𝑉| ) Note: though the O(|E|) loop is nested in the O(|V|) loop, we visit each edge at most twice rather than |V| times. This is why the runtime of the second half of the algorithm is O((V * log|V|) + (E * log|V|))
Dijkstra ain’t perfect! Tuesday April 5, 2016 Dijkstra ain’t perfect! In 𝑂( (|𝑉|+|𝐸|) 𝑙𝑜𝑔 |𝑉| ) time we can now find the shortest path between any two nodes in a graph no matter the edge weights! … or can we? The algorithm fails with negative edge weights Dijkstra’s returns the path [A, C, D] (why?) when the correct path is [A, B, C, D]! We’ll try to fix this next… D A B C 2 - 7 5 8 Start End
Tuesday April 5, 2016 Negative Edge Weights Negative edge weights present a problem for Dijkstra’s algorithm An even bigger problem is negative cycles like the one below, since this means there doesn’t exist a true shortest path! D A B C -10 5 15 3 Start End
Tuesday April 5, 2016 Currency Arbitrage As a bit of a tangent, consider a graph with currencies as nodes and exchange rates as the edges If a negative cycle exists, then one can make a profit purely by exchanging money! This is called currency arbitrage
Bellman-Ford Algorithm Tuesday April 5, 2016 Bellman-Ford Algorithm To handle graphs with negative edge weights, we’ll need a new algorithm The Bellman-Ford algorithm is similar to Dijkstra’s but more robust It will return the same output as Dijkstra’s for any graphs with only positive edge weights, but run slower Unlike Dijkstra’s, it correctly generates shortest paths in the presence of negative edge weights
Bellman-Ford Algorithm (2) Tuesday April 5, 2016 Bellman-Ford Algorithm (2) Very similar to Dijkstra’s except that instead of utilizing a priority queue to visit nodes in order, Bellman-Ford exhaustively iterates over every edge |V| times each, ensuring that all negative edge weights propagate This strategy fixes the above example which Dijkstra failed for not evaluating the edges (A, B) and (B, C) D A B C 2 - 7 5 8 Start End
How does it work? Step 1 – Initialize the graph: Tuesday April 5, 2016 How does it work? Step 1 – Initialize the graph: Decorate each node with a previous pointer (set to null) Decorate source with distance of 0, everything else to ∞ Continually update these distances
How does it work? (2) Step 2 – Update (“relax”) edges |V|-1 times: Tuesday April 5, 2016 How does it work? (2) Step 2 – Update (“relax”) edges |V|-1 times: Loop through every edge (u,v) to see if it can be updated Update node v’s distance if u.distance + uv.weight < v.distance. Set v’s previous accordingly
Pseudocode function BellmanFord(V, E, src): for v in V: v.dist = ∞ Tuesday April 5, 2016 Pseudocode function BellmanFord(V, E, src): for v in V: v.dist = ∞ v.prev = null src.dist = 0 repeat |V|-1 times: // Relax edges repeatedly for each edge (u,v) in E: if u.dist + cost(u,v) < v.dist: v.dist = u.dist + cost(u,v) v.prev = u
Tuesday April 5, 2016 Negative Edge Cycles How can we enhance the algorithm to detect negative edge cycles? Simply try relaxing the edges one more time, and if anything changes, there must be a negative cycle! This strategy would allow a currency exploiter to find negative cycles in exchange rates and turn a profit!
Pseudocode function BellmanFord(V, E, src): for v in V: v.dist = ∞ Tuesday April 5, 2016 Pseudocode function BellmanFord(V, E, src): for v in V: v.dist = ∞ v.prev = null src.dist = 0 repeat |V|-1 times: // Relax edges repeatedly for each edge (u,v) in E: if u.dist + cost(u,v) < v.dist: v.dist = u.dist + cost(u,v) v.prev = u // Step 3: check for negative-weight cycles if u.distance + (u,v).weight < v.distance: throw NegativeCycleException
Bellman-Ford Example A B C D E 4 2 3 1 -5 5 A B C D E ∞ Tuesday April 5, 2016 Bellman-Ford Example A B C D E 4 2 3 1 -5 5 A B C D E ∞
Bellman-Ford Example A B C D E 4 2 3 1 -5 5 A B C D E 4 2 ∞ Tuesday April 5, 2016 Bellman-Ford Example A B C D E 4 2 3 1 -5 5 A B C D E 4 2 ∞
Bellman-Ford Example A B C D E 4 2 3 1 -5 5 A B C D E 3 2 6 Tuesday April 5, 2016 Bellman-Ford Example A B C D E 4 2 3 1 -5 5 A B C D E 3 2 6
Bellman-Ford Example A B C D E 4 2 3 1 -5 5 A B C D E 3 2 1 6 Tuesday April 5, 2016 Bellman-Ford Example A B C D E 4 2 3 1 -5 5 A B C D E 3 2 1 6
Bellman-Ford Example A B C D E 4 2 3 1 -5 5 A B C D E ∞ 4 2 3 6 1 Tuesday April 5, 2016 Bellman-Ford Example A B C D E 4 2 3 1 -5 5 A B C D E ∞ 4 2 3 6 1 Go through each row. update all vertices using the values from the last pass (values don’t change during a pass – all reset at the end) Each row is one of V-1 iterations Each vertex is updating using values from the previous iteration
Why at Most |V|-1 Passes? What is longest shortest path possible? Tuesday April 5, 2016 Why at Most |V|-1 Passes? What is longest shortest path possible? The longest shortest path contains every vertex but has no loops because a loop would make it longer than the shortest path of the same length! Suppose the path is S P1 P2 … P|V|-1 First iteration guaranteed to correctly decorate P1, second guaranteed to correctly decorate P2, …, |V|-1th guaranteed to correctly decorate P|V|-1 Therefore, in |V|-1 iterations, even the longest shortest path will be properly evaluated Has all vertices Has no loops – why? go back to last slide and draw out some longest shortest path candidates A B C E D A B C B C B C ... – show no loops but could have all the vertices so |V| vertices and |V|-1 edges S P1 P2 P3 … P|V|-1 First pass guaranteed to correctly decorate P1. Second pass guaranteed to correctly decorate P2. |V|-1th pass guaranteed to correctly decorate P|V|-1.
Bellman-Ford Runtime Analysis Tuesday April 5, 2016 Bellman-Ford Runtime Analysis function BellmanFord(V, E, src): for v in V: // O(|V|) v.dist = ∞ v.prev = null src.dist = 0 repeat |V|-1 times: // O(|V|) for each edge (u,v) in E: // O(|E|) if u.dist + cost(u,v) < v.dist: v.dist = u.dist + cost(u,v) v.prev = u Overall time complexity: O(|V|*|E|)
Readings Online Simulation Dasgupta Sections 4.1-4.5 Tuesday April 5, 2016 Readings Online Simulation http://optlab- server.sce.carleton.ca/POAnimations2007/DijkstrasAlgo.h tml Great step-by-step interactive simulation of the algorithm Dasgupta Sections 4.1-4.5 Excellent summaries of shortest path finding algorithms on graphs (focusing on breadth first search and Dijkstra’s) Triangular Arbitrage https://en.wikipedia.org/wiki/Triangular_arbitrage Interesting reading on the abuses of exchange rates by representing currencies with graph theory Dasgupta Sections 4.6 Great summary of Bellman-Ford algorithm which handles negative edge weights