mirror of
https://github.com/mfocko/blog.git
synced 2024-11-24 22:11:54 +01:00
1 line
No EOL
24 KiB
JavaScript
1 line
No EOL
24 KiB
JavaScript
"use strict";(self.webpackChunkfi=self.webpackChunkfi||[]).push([[3713],{38961:(e,t,s)=>{s.r(t),s.d(t,{assets:()=>h,contentTitle:()=>r,default:()=>c,frontMatter:()=>i,metadata:()=>o,toc:()=>l});var n=s(85893),a=s(11151);const i={id:"dijkstra",slug:"/paths/bf-to-astar/dijkstra",title:"Dijkstra's algorithm",description:"Moving from Bellman-Ford into the Dijsktra's algorithm.\n",tags:["cpp","dynamic programming","greedy","dijkstra"],last_update:{date:new Date("2024-01-03T00:00:00.000Z")}},r=void 0,o={id:"paths/2024-01-01-bf-to-astar/dijkstra",title:"Dijkstra's algorithm",description:"Moving from Bellman-Ford into the Dijsktra's algorithm.\n",source:"@site/algorithms/11-paths/2024-01-01-bf-to-astar/02-dijkstra.md",sourceDirName:"11-paths/2024-01-01-bf-to-astar",slug:"/paths/bf-to-astar/dijkstra",permalink:"/algorithms/paths/bf-to-astar/dijkstra",draft:!1,unlisted:!1,editUrl:"https://github.com/mfocko/blog/tree/main/algorithms/11-paths/2024-01-01-bf-to-astar/02-dijkstra.md",tags:[{label:"cpp",permalink:"/algorithms/tags/cpp"},{label:"dynamic programming",permalink:"/algorithms/tags/dynamic-programming"},{label:"greedy",permalink:"/algorithms/tags/greedy"},{label:"dijkstra",permalink:"/algorithms/tags/dijkstra"}],version:"current",lastUpdatedAt:170424e4,formattedLastUpdatedAt:"Jan 3, 2024",sidebarPosition:2,frontMatter:{id:"dijkstra",slug:"/paths/bf-to-astar/dijkstra",title:"Dijkstra's algorithm",description:"Moving from Bellman-Ford into the Dijsktra's algorithm.\n",tags:["cpp","dynamic programming","greedy","dijkstra"],last_update:{date:"2024-01-03T00:00:00.000Z"}},sidebar:"autogeneratedBar",previous:{title:"BF",permalink:"/algorithms/paths/bf-to-astar/bf"},next:{title:"A* algorithm",permalink:"/algorithms/paths/bf-to-astar/astar"}},h={},l=[{value:"Intro",id:"intro",level:2},{value:"Dijkstra's algorithm",id:"dijkstras-algorithm",level:2},{value:"Short description",id:"short-description",level:2},{value:"Used techniques",id:"used-techniques",level:2},{value:"Implementation",id:"implementation",level:2},{value:"Time complexity",id:"time-complexity",level:2},{value:"Running the Dijkstra",id:"running-the-dijkstra",level:2},{value:"Busting the myth about looping Dijkstra",id:"busting-the-myth-about-looping-dijkstra",level:2},{value:"Summary",id:"summary",level:2}];function m(e){const t={admonition:"admonition",annotation:"annotation",blockquote:"blockquote",code:"code",em:"em",h2:"h2",img:"img",math:"math",mi:"mi",mn:"mn",mo:"mo",mrow:"mrow",msup:"msup",p:"p",pre:"pre",semantics:"semantics",span:"span",strong:"strong",...(0,a.a)(),...e.components};return(0,n.jsxs)(n.Fragment,{children:[(0,n.jsx)(t.h2,{id:"intro",children:"Intro"}),"\n",(0,n.jsx)(t.p,{children:"Let's rewind back to the small argument in the previous post about the fact that\nwe can safely bound the amount of iterations with relaxations being done."}),"\n",(0,n.jsxs)(t.p,{children:["We have said that assuming the worst-case scenario (bad order of relaxations) we\n",(0,n.jsx)(t.strong,{children:"need"})," at most ",(0,n.jsxs)(t.span,{className:"katex",children:[(0,n.jsx)(t.span,{className:"katex-mathml",children:(0,n.jsx)(t.math,{xmlns:"http://www.w3.org/1998/Math/MathML",children:(0,n.jsxs)(t.semantics,{children:[(0,n.jsxs)(t.mrow,{children:[(0,n.jsx)(t.mi,{mathvariant:"normal",children:"\u2223"}),(0,n.jsx)(t.mi,{children:"V"}),(0,n.jsx)(t.mi,{mathvariant:"normal",children:"\u2223"}),(0,n.jsx)(t.mo,{children:"\u2212"}),(0,n.jsx)(t.mn,{children:"1"})]}),(0,n.jsx)(t.annotation,{encoding:"application/x-tex",children:"\\vert V \\vert - 1"})]})})}),(0,n.jsxs)(t.span,{className:"katex-html","aria-hidden":"true",children:[(0,n.jsxs)(t.span,{className:"base",children:[(0,n.jsx)(t.span,{className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,n.jsx)(t.span,{className:"mord",children:"\u2223"}),(0,n.jsx)(t.span,{className:"mord mathnormal",style:{marginRight:"0.22222em"},children:"V"}),(0,n.jsx)(t.span,{className:"mord",children:"\u2223"}),(0,n.jsx)(t.span,{className:"mspace",style:{marginRight:"0.2222em"}}),(0,n.jsx)(t.span,{className:"mbin",children:"\u2212"}),(0,n.jsx)(t.span,{className:"mspace",style:{marginRight:"0.2222em"}})]}),(0,n.jsxs)(t.span,{className:"base",children:[(0,n.jsx)(t.span,{className:"strut",style:{height:"0.6444em"}}),(0,n.jsx)(t.span,{className:"mord",children:"1"})]})]})]})," iterations over all edges. We've used that\nto our advantage to ",(0,n.jsx)(t.em,{children:"bound"})," the iterations instead of the ",(0,n.jsx)(t.code,{children:"do-while"})," loop that\nwas a risk given the possibility of the infinite loop (when negative loops are\npresent in the graph)."]}),"\n",(0,n.jsxs)(t.admonition,{type:"tip",children:[(0,n.jsxs)(t.p,{children:["We could've possibly used both ",(0,n.jsx)(t.em,{children:"boolean flag"})," to denote that some relaxation has\nhappened and the upper bound of iterations, for some graphs that would result in\nfaster termination."]}),(0,n.jsx)(t.p,{children:"Using only the upper bound we try to relax edges even though we can't."})]}),"\n",(0,n.jsx)(t.p,{children:"Now the question arises, could we leverage this somehow in a different way? What\nif we used it to improve the algorithm instead of just bounding the iterations?\nWould that be even possible?"}),"\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.strong,{children:"Yes, it would!"})," And that's when ",(0,n.jsx)(t.em,{children:"Dijkstra's algorithm"})," comes in."]}),"\n",(0,n.jsx)(t.h2,{id:"dijkstras-algorithm",children:"Dijkstra's algorithm"}),"\n",(0,n.jsxs)(t.p,{children:["I'll start with a well-known meme about Dijkstra's algorithm:\n",(0,n.jsx)(t.img,{alt:"Dijkstra's algorithm meme",src:s(3260).Z+"",width:"960",height:"724"})]}),"\n",(0,n.jsx)(t.p,{children:"And then follow up on that with the actual backstory from Dijkstra himself:"}),"\n",(0,n.jsxs)(t.blockquote,{children:["\n",(0,n.jsx)(t.p,{children:"What is the shortest way to travel from Rotterdam to Groningen, in general:\nfrom given city to given city. It is the algorithm for the shortest path,\nwhich I designed in about twenty minutes. One morning I was shopping in\nAmsterdam with my young fianc\xe9e, and tired, we sat down on the caf\xe9 terrace to\ndrink a cup of coffee and I was just thinking about whether I could do this,\nand I then designed the algorithm for the shortest path. As I said, it was\na twenty-minute invention. In fact, it was published in '59, three years\nlater. The publication is still readable, it is, in fact, quite nice. One of\nthe reasons that it is so nice was that I designed it without pencil and\npaper. I learned later that one of the advantages of designing without pencil\nand paper is that you are almost forced to avoid all avoidable complexities.\nEventually, that algorithm became to my great amazement, one of the\ncornerstones of my fame."}),"\n",(0,n.jsx)(t.p,{children:"\u2014 Edsger Dijkstra, in an interview with Philip L. Frana, Communications of the\nACM, 2001"}),"\n"]}),"\n",(0,n.jsx)(t.admonition,{title:"Precondition",type:"warning",children:(0,n.jsxs)(t.p,{children:["As our own na\xefve algorithm, Dijkstra's algorithm has a precondition that places\na requirement of ",(0,n.jsx)(t.em,{children:"no edges with negative weights"})," in the graph. This\nprecondition is required because of the nature of the algorithm that requires\nmonotonically non-decreasing changes in the costs of shortest paths."]})}),"\n",(0,n.jsx)(t.h2,{id:"short-description",children:"Short description"}),"\n",(0,n.jsx)(t.p,{children:"Let's have a brief look at the pseudocode taken from the Wikipedia:"}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{children:"function Dijkstra(Graph, source):\n for each vertex v in Graph.Vertices:\n dist[v] \u2190 INFINITY\n prev[v] \u2190 UNDEFINED\n add v to Q\n dist[source] \u2190 0\n\n while Q is not empty:\n u \u2190 vertex in Q with min dist[u]\n remove u from Q\n\n for each neighbor v of u still in Q:\n alt \u2190 dist[u] + Graph.Edges(u, v)\n if alt < dist[v]:\n dist[v] \u2190 alt\n prev[v] \u2190 u\n\n return dist[], prev[]\n"})}),"\n",(0,n.jsx)(t.p,{children:"Dijkstra's algorithm works in such way that it always tries to find the shortest\npaths from a vertex to which it already has a shortest path. This may result in\nfinding the shortest path to another vertex, or at least some path, till further\nrelaxation."}),"\n",(0,n.jsxs)(t.p,{children:["Given that we need to ",(0,n.jsx)(t.strong,{children:"always"})," choose the ",(0,n.jsx)(t.em,{children:"cheapest"})," vertex, we can use a min\nheap to our advantage which can further improve the time complexity of the\nalgorithm."]}),"\n",(0,n.jsx)(t.h2,{id:"used-techniques",children:"Used techniques"}),"\n",(0,n.jsxs)(t.p,{children:["This algorithm leverages the ",(0,n.jsx)(t.em,{children:"dynamic programming"})," technique that has already\nbeen mentioned with regards to the ",(0,n.jsx)(t.em,{children:"Bellman-Ford"})," algorithm and also ",(0,n.jsx)(t.em,{children:"greedy"}),"\ntechnique. Let's talk about them both!"]}),"\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.em,{children:"Dynamic programming"})," technique comes from the fact that we are continuously\nbuilding on top of the shortest paths that we have found so far. We slowly build\nthe shortest paths from the given source vertex to all other vertices that are\nreachable."]}),"\n",(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.em,{children:"Greedy"})," technique is utilized in such way that Dijkstra's algorithm always\nimproves the shortest paths from the vertex that is the closest, i.e. it tries\nextending the shortest path to some vertex by appending an edge, such extended\npath may (or may not) be the shortest path to another vertex."]}),"\n",(0,n.jsx)(t.admonition,{title:"Greedy algorithms",type:"tip",children:(0,n.jsxs)(t.p,{children:[(0,n.jsx)(t.em,{children:"Greedy algorithms"})," are algorithms that choose the most optimal action\n",(0,n.jsx)(t.strong,{children:"at the moment"}),"."]})}),"\n",(0,n.jsx)(t.p,{children:"The reason why the algorithm requires no edges with negative weights comes from\nthe fact that it's greedy. By laying the requirement of non-negative weights in\nthe graph we are guaranteed that at any given moment of processing outgoing\nedges from a vertex, we already have a shortest path to the given vertex. This\nmeans that either this is the shortest path, or there is some other vertex that\nmay have a higher cost, but the outgoing edge compensates for it."}),"\n",(0,n.jsx)(t.h2,{id:"implementation",children:"Implementation"}),"\n",(0,n.jsx)(t.p,{children:"Firstly we need to have some priority queue wrappers. C++ itself offers\nfunctions that can be used for maintaining max heaps. They also have generalized\nversion with any ordering, in our case we need reverse ordering, because we need\nthe min heap."}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-cpp",children:"using pqueue_item_t = std::pair<int, vertex_t>;\nusing pqueue_t = std::vector<pqueue_item_t>;\n\nauto pushq(pqueue_t& q, pqueue_item_t v) -> void {\n q.push_back(v);\n std::push_heap(q.begin(), q.end(), std::greater<>{});\n}\n\nauto popq(pqueue_t& q) -> std::optional<pqueue_item_t> {\n if (q.empty()) {\n return {};\n }\n\n std::pop_heap(q.begin(), q.end(), std::greater<>{});\n pqueue_item_t top = q.back();\n q.pop_back();\n\n return std::make_optional(top);\n}\n"})}),"\n",(0,n.jsx)(t.p,{children:"And now we can finally move to the actual implementation of the Dijkstra's\nalgorithm:"}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{className:"language-cpp",children:"auto dijkstra(const graph& g, const vertex_t& source)\n -> std::vector<std::vector<int>> {\n // make sure that \u2039source\u203a exists\n assert(g.has(source));\n\n // initialize the distances\n std::vector<std::vector<int>> distances(\n g.height(), std::vector(g.width(), graph::unreachable()));\n\n // initialize the visited\n std::vector<std::vector<bool>> visited(g.height(),\n std::vector(g.width(), false));\n\n // \u2039source\u203a destination denotes the beginning where the cost is 0\n auto [sx, sy] = source;\n distances[sy][sx] = 0;\n\n pqueue_t priority_queue{std::make_pair(0, source)};\n std::optional<pqueue_item_t> item{};\n while ((item = popq(priority_queue))) {\n auto [cost, u] = *item;\n auto [x, y] = u;\n\n // we have already found the shortest path\n if (visited[y][x]) {\n continue;\n }\n visited[y][x] = true;\n\n for (const auto& [dx, dy] : DIRECTIONS) {\n auto v = std::make_pair(x + dx, y + dy);\n auto cost = g.cost(u, v);\n\n // if we can move to the cell and it's better, relax\xb9 it and update queue\n if (cost != graph::unreachable() &&\n distances[y][x] + cost < distances[y + dy][x + dx]) {\n distances[y + dy][x + dx] = distances[y][x] + cost;\n pushq(priority_queue, std::make_pair(distances[y + dy][x + dx], v));\n }\n }\n }\n\n return distances;\n}\n"})}),"\n",(0,n.jsx)(t.h2,{id:"time-complexity",children:"Time complexity"}),"\n",(0,n.jsx)(t.p,{children:"The time complexity of Dijkstra's algorithm differs based on the backing data\nstructure."}),"\n",(0,n.jsxs)(t.p,{children:["The original implementation doesn't leverage the heap which results in\nrepetitive ",(0,n.jsx)(t.em,{children:"look up"})," of the \u201cclosest\u201d vertex, hence we get the following\nworst-case time complexity in the ",(0,n.jsx)(t.em,{children:"Bachmann-Landau"})," notation:"]}),"\n",(0,n.jsx)(t.span,{className:"katex-display",children:(0,n.jsxs)(t.span,{className:"katex",children:[(0,n.jsx)(t.span,{className:"katex-mathml",children:(0,n.jsx)(t.math,{xmlns:"http://www.w3.org/1998/Math/MathML",display:"block",children:(0,n.jsxs)(t.semantics,{children:[(0,n.jsxs)(t.mrow,{children:[(0,n.jsx)(t.mi,{mathvariant:"normal",children:"\u0398"}),(0,n.jsx)(t.mo,{stretchy:"false",children:"("}),(0,n.jsx)(t.mi,{mathvariant:"normal",children:"\u2223"}),(0,n.jsx)(t.mi,{children:"V"}),(0,n.jsxs)(t.msup,{children:[(0,n.jsx)(t.mi,{mathvariant:"normal",children:"\u2223"}),(0,n.jsx)(t.mn,{children:"2"})]}),(0,n.jsx)(t.mo,{stretchy:"false",children:")"})]}),(0,n.jsx)(t.annotation,{encoding:"application/x-tex",children:"\\Theta(\\vert V \\vert^2)"})]})})}),(0,n.jsx)(t.span,{className:"katex-html","aria-hidden":"true",children:(0,n.jsxs)(t.span,{className:"base",children:[(0,n.jsx)(t.span,{className:"strut",style:{height:"1.1141em",verticalAlign:"-0.25em"}}),(0,n.jsx)(t.span,{className:"mord",children:"\u0398"}),(0,n.jsx)(t.span,{className:"mopen",children:"("}),(0,n.jsx)(t.span,{className:"mord",children:"\u2223"}),(0,n.jsx)(t.span,{className:"mord mathnormal",style:{marginRight:"0.22222em"},children:"V"}),(0,n.jsxs)(t.span,{className:"mord",children:[(0,n.jsx)(t.span,{className:"mord",children:"\u2223"}),(0,n.jsx)(t.span,{className:"msupsub",children:(0,n.jsx)(t.span,{className:"vlist-t",children:(0,n.jsx)(t.span,{className:"vlist-r",children:(0,n.jsx)(t.span,{className:"vlist",style:{height:"0.8641em"},children:(0,n.jsxs)(t.span,{style:{top:"-3.113em",marginRight:"0.05em"},children:[(0,n.jsx)(t.span,{className:"pstrut",style:{height:"2.7em"}}),(0,n.jsx)(t.span,{className:"sizing reset-size6 size3 mtight",children:(0,n.jsx)(t.span,{className:"mord mtight",children:"2"})})]})})})})})]}),(0,n.jsx)(t.span,{className:"mclose",children:")"})]})})]})}),"\n",(0,n.jsx)(t.p,{children:"If we turn our attention to the backing data structure, we always want the\n\u201ccheapest\u201d vertex, that's why we can use the min heap, given that we use\nFibonacci heap we can achieve the following amortized time complexity:"}),"\n",(0,n.jsx)(t.span,{className:"katex-display",children:(0,n.jsxs)(t.span,{className:"katex",children:[(0,n.jsx)(t.span,{className:"katex-mathml",children:(0,n.jsx)(t.math,{xmlns:"http://www.w3.org/1998/Math/MathML",display:"block",children:(0,n.jsxs)(t.semantics,{children:[(0,n.jsxs)(t.mrow,{children:[(0,n.jsx)(t.mi,{mathvariant:"script",children:"O"}),(0,n.jsx)(t.mo,{stretchy:"false",children:"("}),(0,n.jsx)(t.mi,{mathvariant:"normal",children:"\u2223"}),(0,n.jsx)(t.mi,{children:"E"}),(0,n.jsx)(t.mi,{mathvariant:"normal",children:"\u2223"}),(0,n.jsx)(t.mo,{children:"+"}),(0,n.jsx)(t.mi,{mathvariant:"normal",children:"\u2223"}),(0,n.jsx)(t.mi,{children:"V"}),(0,n.jsx)(t.mi,{mathvariant:"normal",children:"\u2223"}),(0,n.jsx)(t.mo,{children:"\u22c5"}),(0,n.jsx)(t.mi,{children:"log"}),(0,n.jsx)(t.mo,{children:"\u2061"}),(0,n.jsxs)(t.mrow,{children:[(0,n.jsx)(t.mi,{mathvariant:"normal",children:"\u2223"}),(0,n.jsx)(t.mi,{children:"V"}),(0,n.jsx)(t.mi,{mathvariant:"normal",children:"\u2223"})]}),(0,n.jsx)(t.mo,{stretchy:"false",children:")"})]}),(0,n.jsx)(t.annotation,{encoding:"application/x-tex",children:"\\mathcal{O}(\\vert E \\vert + \\vert V \\vert \\cdot \\log{\\vert V \\vert})"})]})})}),(0,n.jsxs)(t.span,{className:"katex-html","aria-hidden":"true",children:[(0,n.jsxs)(t.span,{className:"base",children:[(0,n.jsx)(t.span,{className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,n.jsx)(t.span,{className:"mord mathcal",style:{marginRight:"0.02778em"},children:"O"}),(0,n.jsx)(t.span,{className:"mopen",children:"("}),(0,n.jsx)(t.span,{className:"mord",children:"\u2223"}),(0,n.jsx)(t.span,{className:"mord mathnormal",style:{marginRight:"0.05764em"},children:"E"}),(0,n.jsx)(t.span,{className:"mord",children:"\u2223"}),(0,n.jsx)(t.span,{className:"mspace",style:{marginRight:"0.2222em"}}),(0,n.jsx)(t.span,{className:"mbin",children:"+"}),(0,n.jsx)(t.span,{className:"mspace",style:{marginRight:"0.2222em"}})]}),(0,n.jsxs)(t.span,{className:"base",children:[(0,n.jsx)(t.span,{className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,n.jsx)(t.span,{className:"mord",children:"\u2223"}),(0,n.jsx)(t.span,{className:"mord mathnormal",style:{marginRight:"0.22222em"},children:"V"}),(0,n.jsx)(t.span,{className:"mord",children:"\u2223"}),(0,n.jsx)(t.span,{className:"mspace",style:{marginRight:"0.2222em"}}),(0,n.jsx)(t.span,{className:"mbin",children:"\u22c5"}),(0,n.jsx)(t.span,{className:"mspace",style:{marginRight:"0.2222em"}})]}),(0,n.jsxs)(t.span,{className:"base",children:[(0,n.jsx)(t.span,{className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,n.jsxs)(t.span,{className:"mop",children:["lo",(0,n.jsx)(t.span,{style:{marginRight:"0.01389em"},children:"g"})]}),(0,n.jsx)(t.span,{className:"mspace",style:{marginRight:"0.1667em"}}),(0,n.jsxs)(t.span,{className:"mord",children:[(0,n.jsx)(t.span,{className:"mord",children:"\u2223"}),(0,n.jsx)(t.span,{className:"mord mathnormal",style:{marginRight:"0.22222em"},children:"V"}),(0,n.jsx)(t.span,{className:"mord",children:"\u2223"})]}),(0,n.jsx)(t.span,{className:"mclose",children:")"})]})]})]})}),"\n",(0,n.jsx)(t.admonition,{title:"Fibonacci heap",type:"tip",children:(0,n.jsxs)(t.p,{children:["Fibonacci heap is known as the heap that provides ",(0,n.jsxs)(t.span,{className:"katex",children:[(0,n.jsx)(t.span,{className:"katex-mathml",children:(0,n.jsx)(t.math,{xmlns:"http://www.w3.org/1998/Math/MathML",children:(0,n.jsxs)(t.semantics,{children:[(0,n.jsxs)(t.mrow,{children:[(0,n.jsx)(t.mi,{mathvariant:"normal",children:"\u0398"}),(0,n.jsx)(t.mo,{stretchy:"false",children:"("}),(0,n.jsx)(t.mn,{children:"1"}),(0,n.jsx)(t.mo,{stretchy:"false",children:")"})]}),(0,n.jsx)(t.annotation,{encoding:"application/x-tex",children:"\\Theta(1)"})]})})}),(0,n.jsx)(t.span,{className:"katex-html","aria-hidden":"true",children:(0,n.jsxs)(t.span,{className:"base",children:[(0,n.jsx)(t.span,{className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,n.jsx)(t.span,{className:"mord",children:"\u0398"}),(0,n.jsx)(t.span,{className:"mopen",children:"("}),(0,n.jsx)(t.span,{className:"mord",children:"1"}),(0,n.jsx)(t.span,{className:"mclose",children:")"})]})})]})," ",(0,n.jsx)(t.strong,{children:"amortized"}),"\ninsertion and ",(0,n.jsxs)(t.span,{className:"katex",children:[(0,n.jsx)(t.span,{className:"katex-mathml",children:(0,n.jsx)(t.math,{xmlns:"http://www.w3.org/1998/Math/MathML",children:(0,n.jsxs)(t.semantics,{children:[(0,n.jsxs)(t.mrow,{children:[(0,n.jsx)(t.mi,{mathvariant:"script",children:"O"}),(0,n.jsx)(t.mo,{stretchy:"false",children:"("}),(0,n.jsx)(t.mi,{children:"log"}),(0,n.jsx)(t.mo,{children:"\u2061"}),(0,n.jsx)(t.mi,{children:"n"}),(0,n.jsx)(t.mo,{stretchy:"false",children:")"})]}),(0,n.jsx)(t.annotation,{encoding:"application/x-tex",children:"\\mathcal{O}(\\log{n})"})]})})}),(0,n.jsx)(t.span,{className:"katex-html","aria-hidden":"true",children:(0,n.jsxs)(t.span,{className:"base",children:[(0,n.jsx)(t.span,{className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,n.jsx)(t.span,{className:"mord mathcal",style:{marginRight:"0.02778em"},children:"O"}),(0,n.jsx)(t.span,{className:"mopen",children:"("}),(0,n.jsxs)(t.span,{className:"mop",children:["lo",(0,n.jsx)(t.span,{style:{marginRight:"0.01389em"},children:"g"})]}),(0,n.jsx)(t.span,{className:"mspace",style:{marginRight:"0.1667em"}}),(0,n.jsx)(t.span,{className:"mord",children:(0,n.jsx)(t.span,{className:"mord mathnormal",children:"n"})}),(0,n.jsx)(t.span,{className:"mclose",children:")"})]})})]})," ",(0,n.jsx)(t.strong,{children:"amortized"})," removal of the top (either\nmin or max)."]})}),"\n",(0,n.jsx)(t.h2,{id:"running-the-dijkstra",children:"Running the Dijkstra"}),"\n",(0,n.jsx)(t.p,{children:"Let's run our code:"}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{children:"Normal cost: 1\nVortex cost: 5\nGraph:\n#############\n#..#..*.*.**#\n##***.....**#\n#..########.#\n#...###...#.#\n#..#...##.#.#\n#..#.*.#..#.#\n#D...#....#.#\n########*.*.#\n#S..........#\n#############\n[Finite BF] Cost: 22\n[Bellman-Ford] Cost: 22\n[Dijkstra] Cost: 22\n"})}),"\n",(0,n.jsx)(t.p,{children:"OK, so it seems to be working just fine. Now the question arises:"}),"\n",(0,n.jsxs)(t.blockquote,{children:["\n",(0,n.jsx)(t.p,{children:"What happens when we have negative weights in our graph?"}),"\n"]}),"\n",(0,n.jsx)(t.h2,{id:"busting-the-myth-about-looping-dijkstra",children:"Busting the myth about looping Dijkstra"}),"\n",(0,n.jsxs)(t.p,{children:["One of the very common misconception about Dijkstra's algorithm is that it loops\ninfinitely when you have negative weights or loops in the graph. Well, if we use\nour ",(0,n.jsx)(t.em,{children:"propelling vortices"}),", not only we have the negative weights, but also the\nnegative loops. Let's run our code! Our first na\xefve approach was actually\nlooping:"]}),"\n",(0,n.jsx)(t.pre,{children:(0,n.jsx)(t.code,{children:"Normal cost: 1\nVortex cost: -1\nGraph:\n#############\n#..#..*.*.**#\n##***.....**#\n#..########.#\n#...###...#.#\n#..#...##.#.#\n#..#.*.#..#.#\n#D...#....#.#\n########*.*.#\n#S..........#\n#############\n[Finite BF] Cost: -240\n[Bellman-Ford] Found a negative loop\n[Bellman-Ford] Cost: -240\n[Dijkstra] Cost: 14\n"})}),"\n",(0,n.jsxs)(t.p,{children:["Well, it definitely doesn't loop. How much does ",(0,n.jsx)(t.code,{children:"14"})," make sense is a different\nmatter."]}),"\n",(0,n.jsx)(t.admonition,{title:"Variations",type:"info",children:(0,n.jsxs)(t.p,{children:["There are multiple variations of the Dijkstra's algorithm. You ",(0,n.jsx)(t.strong,{children:"can"})," implement\nit in such way that with negative weights or loops it loops infinitely, but it\ncan be countered. In our case we keep the track of the vertices that already got\na shortest path established via the ",(0,n.jsx)(t.code,{children:"visited"}),", that's how even multiple entries\nfor one vertex in the heap are not an issue."]})}),"\n",(0,n.jsx)(t.h2,{id:"summary",children:"Summary"}),"\n",(0,n.jsx)(t.p,{children:"Now we have an algorithm for finding the shortest path that is faster than our\noriginal na\xefve brute-force or Bellman-Ford. However we need to keep in mind its\nrequirement of no negative weights for correct functioning."}),"\n",(0,n.jsx)(t.p,{children:"You can also see how we used our thought process of figuring out the worst-case\ntime complexity for the na\xefve or Bellman-Ford algorithm to improve the original\npath-finding algorithms."})]})}function c(e={}){const{wrapper:t}={...(0,a.a)(),...e.components};return t?(0,n.jsx)(t,{...e,children:(0,n.jsx)(m,{...e})}):m(e)}},3260:(e,t,s)=>{s.d(t,{Z:()=>n});const n=s.p+"assets/images/dijkstra-meme-405d6b8dcc7aec5846fef402abfa8317.jpg"},11151:(e,t,s)=>{s.d(t,{Z:()=>o,a:()=>r});var n=s(67294);const a={},i=n.createContext(a);function r(e){const t=n.useContext(i);return n.useMemo((function(){return"function"==typeof e?e(t):{...t,...e}}),[t,e])}function o(e){let t;return t=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:r(e.components),n.createElement(i.Provider,{value:t},e.children)}}}]); |