{"id":488,"date":"2007-08-10T16:33:00","date_gmt":"2007-08-10T16:33:00","guid":{"rendered":"http:\/\/scientopia.org\/blogs\/goodmath\/2007\/08\/10\/shortest-paths-and-greedy-relaxation\/"},"modified":"2007-08-10T16:33:00","modified_gmt":"2007-08-10T16:33:00","slug":"shortest-paths-and-greedy-relaxation","status":"publish","type":"post","link":"http:\/\/www.goodmath.org\/blog\/2007\/08\/10\/shortest-paths-and-greedy-relaxation\/","title":{"rendered":"Shortest Paths and Greedy Relaxation"},"content":{"rendered":"<p> Another really fun and fundamental weighted graph problem is the <em>shortest path<\/em> problem. The<br \/>\nquestion in: given a connected weighted graph G, what is the shortest path between two vertices<br \/>\nv and w in G?<\/p>\n<p><!--more--><\/p>\n<p> To be precise, the weight of a path is just the sum of the weight of the edges that make up<br \/>\nthe path. The shortest path between two vertices is the path with minimum weight.<\/p>\n<p> There are a ton of solutions to this problem. I&#8217;m going to show one of the easiest to understand ones, which is Djikstra&#8217;s algorithm. Djikstra&#8217;s algorithm for the shortest path combines two very interesting<br \/>\nalgorithmic techniques: greediness, and relaxation. We&#8217;ve already talked about greediness in the context<br \/>\nof the minimum spanning tree: a greedy algorithm is an algorithm which works by aggressively picking<br \/>\noptimum sub-values, and building the result from them. Relaxation is an algorithmic technique where<br \/>\nyou start with a maximum value, and attempt to reduce it. The easiest way to understand relaxation<br \/>\nis by metaphor: suppose you want to find the optimal arrangement of a bunch of pegs connected by springs. A relaxation technique basically moves one peg in each step in a way that reduces the tension in at least one spring without increasing the tension in any of the others. So you&#8217;re continually <em>relaxing<\/em> the tension in the springs, until you can&#8217;t relax it any more.<\/p>\n<p> In Djikstra&#8217;s algorithm, you have a graph G=(V,E,W), and you want to find the shortest path<br \/>\nbetween two vertices a and b. You create a table dist(x) of the minimum distance from a to every  vertex in V so that dist(x) is the distance from a to x, and you set the initial value of dist(a) to 0,<br \/>\nthe initial value of dist(x) for all other vertices in E to infinity; and you create a priority list of vertices, ordered by their distance from a.<\/p>\n<p> Then, in each step, you look for the vertex n in E that has the <em>smallest<\/em> value of<br \/>\ndist(n), and you remove that vertex from the priority list. Then, for each vertex m that is adjacent to n, if the path from a to m through n is shorter<br \/>\nthan dist(m), set dist(m) = dist(n) + W(n,m). (This is called <em>relaxing<\/em> the path from a to m.)\n<\/p>\n<p> When you get to the point that the next vertex selected in the priority list is b, then you&#8217;re done &#8211; you know the shortest path.<\/p>\n<p> In pseudo-code:<\/p>\n<pre>\ndef shortestPath(G,a,b):\ndist = Map from vertices(G) to distance\nprev = Map from vertices(G) to vertices(G)\nfor x in vertices(G):\nif x == a:\ndist[x] = 0\nelse:\ndist[x] = &infin;\nend\npriorityList = vertices(G) ordered by distance from a\nfor closest = priorityList.nextVertex() != b:\nfor v adjacent to closest:\nif dist[v] &lt; dist(closest) + weight(closest,v):\ndist[v] = dist(closest) + weight(closest,v)\nprev[v] = closest\nend\nend\nend\n<\/pre>\n<p> When the algorithm terminates, you just trace back through prev to get the shortest path from a to b.\n<\/p>\n<p> So what&#8217;s the complexity of this?<\/p>\n<ul>\n<li> In the worst case, b could be the last vertex extracted from the priority list, in which case<br \/>\nwe would end up running the outer loop once for each vertex. So there&#8217;s a maximum of N iterations<br \/>\nof that loop.<\/li>\n<li> In the inner loop, we need to look at every edge from the selected vertex. That&#8217;s O(E). <\/li>\n<li> To re-order the priority list after removing an element from it takes, at most, N steps.<\/li>\n<\/ul>\n<p> So, we&#8217;re looking at O(|V|<sup>2<\/sup>|E|).<\/p>\n<p> That&#8217;s not the best we can do; by adopting some hairy data structures for the priority queue,<br \/>\nwe can reduce the running time to O(|V|log|V||E|). But the structures to do that have a pretty high<br \/>\nconstants, so they only really pay off for large, sparse graphs.<\/p>\n<p> Let&#8217;s trace through an example. Suppose we want to find the shortest path from A to M in the following graph.<\/p>\n<p><img data-recalc-dims=\"1\" loading=\"lazy\" decoding=\"async\" alt=\"shortest-path-ex.png\" src=\"https:\/\/i0.wp.com\/scientopia.org\/img-archive\/goodmath\/img_246.png?resize=326%2C236\" width=\"326\" height=\"236\" \/><\/p>\n<p> First, we set all weights to infinity, except A to A, all predecessors to empty, and we&#8217;ll compute<br \/>\nthe weights for the priority list. The following shows the initial state of all of these.<\/p>\n<table>\n<tr>\n<th><\/th>\n<th>A<\/th>\n<th>B<\/th>\n<th>C<\/th>\n<th>D<\/th>\n<th>E<\/th>\n<th>F<\/th>\n<th>G<\/th>\n<th>H<\/th>\n<th>I<\/th>\n<th>J<\/th>\n<th>K<\/th>\n<th>L<\/th>\n<th>M<\/th>\n<\/tr>\n<tr>\n<td>Dist<\/td>\n<td>0<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<\/tr>\n<tr>\n<td>Pred<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<\/tr>\n<tr>\n<td>Priority<\/td>\n<td>0<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<\/tr>\n<\/table>\n<p>\tNow, we pick something off  the priority list. The highest priority is 0, for A itself. So we pick A, and<br \/>\nfrom A, we can give distances and priorities to B and D.<\/p>\n<table>\n<tr>\n<th><\/th>\n<th>A<\/th>\n<th>B<\/th>\n<th>C<\/th>\n<th>D<\/th>\n<th>E<\/th>\n<th>F<\/th>\n<th>G<\/th>\n<th>H<\/th>\n<th>I<\/th>\n<th>J<\/th>\n<th>K<\/th>\n<th>L<\/th>\n<th>M<\/th>\n<\/tr>\n<tr>\n<td>Dist<\/td>\n<td>0<\/td>\n<td>3<\/td>\n<td>&infin;<\/td>\n<td>5<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<\/tr>\n<tr>\n<td>Pred<\/td>\n<td>&#8211;<\/td>\n<td>A<\/td>\n<td>&#8211;<\/td>\n<td>A<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<\/tr>\n<tr>\n<td>Priority<\/td>\n<td>X<\/td>\n<td>3<\/td>\n<td>&#8211;<\/td>\n<td>5<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<\/tr>\n<\/table>\n<p> Now, we greedily pick the node with the lowest priority value &#8211; B and process its edges.<\/p>\n<table>\n<tr>\n<th><\/th>\n<th>A<\/th>\n<th>B<\/th>\n<th>C<\/th>\n<th>D<\/th>\n<th>E<\/th>\n<th>F<\/th>\n<th>G<\/th>\n<th>H<\/th>\n<th>I<\/th>\n<th>J<\/th>\n<th>K<\/th>\n<th>L<\/th>\n<th>M<\/th>\n<\/tr>\n<tr>\n<td>Dist<\/td>\n<td>0<\/td>\n<td>3<\/td>\n<td>&infin;<\/td>\n<td>5<\/td>\n<td>14<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<\/tr>\n<tr>\n<td>Pred<\/td>\n<td>&#8211;<\/td>\n<td>A<\/td>\n<td>&#8211;<\/td>\n<td>A<\/td>\n<td>B<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<\/tr>\n<tr>\n<td>Priority<\/td>\n<td>X<\/td>\n<td>X<\/td>\n<td>&#8211;<\/td>\n<td>5<\/td>\n<td>14<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<\/tr>\n<\/table>\n<p> Next lowest priority is D, with 5.<\/p>\n<table>\n<tr>\n<th><\/th>\n<th>A<\/th>\n<th>B<\/th>\n<th>C<\/th>\n<th>D<\/th>\n<th>E<\/th>\n<th>F<\/th>\n<th>G<\/th>\n<th>H<\/th>\n<th>I<\/th>\n<th>J<\/th>\n<th>K<\/th>\n<th>L<\/th>\n<th>M<\/th>\n<\/tr>\n<tr>\n<td>Dist<\/td>\n<td>0<\/td>\n<td>3<\/td>\n<td>12<\/td>\n<td>5<\/td>\n<td>14<\/td>\n<td>13<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<\/tr>\n<tr>\n<td>Pred<\/td>\n<td>&#8211;<\/td>\n<td>A<\/td>\n<td>E<\/td>\n<td>A<\/td>\n<td>B<\/td>\n<td>D<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<\/tr>\n<tr>\n<td>Priority<\/td>\n<td>X<\/td>\n<td>X<\/td>\n<td>12<\/td>\n<td>X<\/td>\n<td>14<\/td>\n<td>13<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<\/tr>\n<\/table>\n<p> Next is C, with priority 12. C gives us a new path to F, but it&#8217;s a path of length 14, which is longer than the path we already<br \/>\nfound.<\/p>\n<table>\n<tr>\n<th><\/th>\n<th>A<\/th>\n<th>B<\/th>\n<th>C<\/th>\n<th>D<\/th>\n<th>E<\/th>\n<th>F<\/th>\n<th>G<\/th>\n<th>H<\/th>\n<th>I<\/th>\n<th>J<\/th>\n<th>K<\/th>\n<th>L<\/th>\n<th>M<\/th>\n<\/tr>\n<tr>\n<td>Dist<\/td>\n<td>0<\/td>\n<td>3<\/td>\n<td>12<\/td>\n<td>5<\/td>\n<td>14<\/td>\n<td>13<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<\/tr>\n<tr>\n<td>Pred<\/td>\n<td>&#8211;<\/td>\n<td>A<\/td>\n<td>E<\/td>\n<td>A<\/td>\n<td>B<\/td>\n<td>D<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<\/tr>\n<tr>\n<td>Priority<\/td>\n<td>X<\/td>\n<td>X<\/td>\n<td>X<\/td>\n<td>X<\/td>\n<td>14<\/td>\n<td>13<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<\/tr>\n<\/table>\n<p> Next is F, with priority 13. Then there&#8217;s a tie, so we pick one randomly, and do E. Then G. After those steps, the table looks like:<\/p>\n<table>\n<tr>\n<th><\/th>\n<th>A<\/th>\n<th>B<\/th>\n<th>C<\/th>\n<th>D<\/th>\n<th>E<\/th>\n<th>F<\/th>\n<th>G<\/th>\n<th>H<\/th>\n<th>I<\/th>\n<th>J<\/th>\n<th>K<\/th>\n<th>L<\/th>\n<th>M<\/th>\n<\/tr>\n<tr>\n<td>Dist<\/td>\n<td>0<\/td>\n<td>3<\/td>\n<td>12<\/td>\n<td>5<\/td>\n<td>14<\/td>\n<td>13<\/td>\n<td>14<\/td>\n<td>18<\/td>\n<td>17<\/td>\n<td>18<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<td>&infin;<\/td>\n<\/tr>\n<tr>\n<td>Pred<\/td>\n<td>&#8211;<\/td>\n<td>A<\/td>\n<td>E<\/td>\n<td>A<\/td>\n<td>B<\/td>\n<td>D<\/td>\n<td>F<\/td>\n<td>E<\/td>\n<td>E<\/td>\n<td>G<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<\/tr>\n<tr>\n<td>Priority<\/td>\n<td>X<\/td>\n<td>X<\/td>\n<td>X<\/td>\n<td>X<\/td>\n<td>X<\/td>\n<td>X<\/td>\n<td>X<\/td>\n<td>18<\/td>\n<td>17<\/td>\n<td>18<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<td>&#8211;<\/td>\n<\/tr>\n<\/table>\n<p> It continues on in the same way, until we get the result   ADFGJKM with a total length of 26.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Another really fun and fundamental weighted graph problem is the shortest path problem. The question in: given a connected weighted graph G, what is the shortest path between two vertices v and w in G?<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[25],"tags":[],"class_list":["post-488","post","type-post","status-publish","format-standard","hentry","category-graph-theory"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_shortlink":"https:\/\/wp.me\/p4lzZS-7S","jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"_links":{"self":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts\/488","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/comments?post=488"}],"version-history":[{"count":0,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/posts\/488\/revisions"}],"wp:attachment":[{"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/media?parent=488"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/categories?post=488"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.goodmath.org\/blog\/wp-json\/wp\/v2\/tags?post=488"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}