mirror of
https://github.com/mfocko/blog.git
synced 2024-11-24 22:11:54 +01:00
1 line
No EOL
48 KiB
JavaScript
1 line
No EOL
48 KiB
JavaScript
"use strict";(self.webpackChunkfi=self.webpackChunkfi||[]).push([[5701],{62535:(e,s,n)=>{n.r(s),n.d(s,{assets:()=>h,contentTitle:()=>r,default:()=>d,frontMatter:()=>i,metadata:()=>l,toc:()=>c});var t=n(85893),a=n(11151);const i={id:"bf",slug:"/paths/bf-to-astar/bf",title:"BF",description:"Solving the shortest path problem with a na\xefve approach that turns into\nsomething.\n",tags:["cpp","brute force","bellman ford","dynamic programming"],last_update:{date:new Date("2024-01-01T00:00:00.000Z")}},r=void 0,l={id:"paths/2024-01-01-bf-to-astar/bf",title:"BF",description:"Solving the shortest path problem with a na\xefve approach that turns into\nsomething.\n",source:"@site/algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md",sourceDirName:"11-paths/2024-01-01-bf-to-astar",slug:"/paths/bf-to-astar/bf",permalink:"/algorithms/paths/bf-to-astar/bf",draft:!1,unlisted:!1,editUrl:"https://github.com/mfocko/blog/tree/main/algorithms/11-paths/2024-01-01-bf-to-astar/01-bf.md",tags:[{label:"cpp",permalink:"/algorithms/tags/cpp"},{label:"brute force",permalink:"/algorithms/tags/brute-force"},{label:"bellman ford",permalink:"/algorithms/tags/bellman-ford"},{label:"dynamic programming",permalink:"/algorithms/tags/dynamic-programming"}],version:"current",lastUpdatedAt:1704067200,formattedLastUpdatedAt:"Jan 1, 2024",sidebarPosition:1,frontMatter:{id:"bf",slug:"/paths/bf-to-astar/bf",title:"BF",description:"Solving the shortest path problem with a na\xefve approach that turns into\nsomething.\n",tags:["cpp","brute force","bellman ford","dynamic programming"],last_update:{date:"2024-01-01T00:00:00.000Z"}},sidebar:"autogeneratedBar",previous:{title:"From BF to A*",permalink:"/algorithms/paths/bf-to-astar"},next:{title:"Dijkstra's algorithm",permalink:"/algorithms/paths/bf-to-astar/dijkstra"}},h={},c=[{value:"Basic idea",id:"basic-idea",level:2},{value:"Na\xefve approach",id:"na\xefve-approach",level:2},{value:"Correctness",id:"correctness",level:3},{value:"Fixing the infinite loop",id:"fixing-the-infinite-loop",level:3},{value:"Bellman-Ford",id:"bellman-ford",level:2},{value:"On the Bellman-Ford",id:"on-the-bellman-ford",level:3},{value:"Time complexity",id:"time-complexity",level:2},{value:"Small refactor",id:"small-refactor",level:2}];function o(e){const s={a:"a",admonition:"admonition",annotation:"annotation",blockquote:"blockquote",code:"code",em:"em",h2:"h2",h3:"h3",hr:"hr",li:"li",math:"math",mi:"mi",mn:"mn",mo:"mo",mrow:"mrow",msub:"msub",ol:"ol",p:"p",pre:"pre",section:"section",semantics:"semantics",span:"span",strong:"strong",sup:"sup",...(0,a.a)(),...e.components};return(0,t.jsxs)(t.Fragment,{children:[(0,t.jsx)(s.h2,{id:"basic-idea",children:"Basic idea"}),"\n",(0,t.jsxs)(s.p,{children:["We will ",(0,t.jsx)(s.em,{children:"ease in"})," with our own algorithm to find the shortest path. We will\nstart by thinking about the ways we can achieve that. If we didn't have the ",(0,t.jsx)(s.code,{children:"*"}),"\ncells, we could've easily run a BFS",(0,t.jsx)(s.sup,{children:(0,t.jsx)(s.a,{href:"#user-content-fn-1",id:"user-content-fnref-1","data-footnote-ref":!0,"aria-describedby":"footnote-label",children:"1"})})," and be done with it. Maybe it is a good\nplace to start, or isn't, there is only one way to find out though."]}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.em,{children:"How does the BFS work?"})," We know the vertex where we start and we know the\nvertex we want to find the shortest path to. Given this knowledge we\nincrementally visit all of our neighbours and we do that over and over until the\ndestination is found",(0,t.jsx)(s.sup,{children:(0,t.jsx)(s.a,{href:"#user-content-fn-2",id:"user-content-fnref-2","data-footnote-ref":!0,"aria-describedby":"footnote-label",children:"2"})}),". Could we leverage this somehow?"]}),"\n",(0,t.jsx)(s.h2,{id:"na\xefve-approach",children:"Na\xefve approach"}),"\n",(0,t.jsxs)(s.p,{children:["Well, we could probably start with all vertices being ",(0,t.jsx)(s.em,{children:"unreachable"})," (having the\nhighest possible price) and try to improve what we've gotten so far until there\nare no improvements. That sounds fine, we shall implement this. Since we are\ngoing on repeat, we will name this function ",(0,t.jsx)(s.code,{children:"bf()"})," as in ",(0,t.jsx)(s.em,{children:"brute-force"}),", cause it\nis trying to find it the hard way:"]}),"\n",(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-cpp",children:"const static std::vector<vertex_t> DIRECTIONS =\n std::vector{std::make_pair(0, 1), std::make_pair(0, -1),\n std::make_pair(1, 0), std::make_pair(-1, 0)};\n\nauto bf(const graph& g, const vertex_t& source, const vertex_t& destination)\n -> int {\n // \u2039source\u203a must be within the bounds\n assert(g.has(source));\n\n // \u2039destination\u203a must be within the bounds\n assert(g.has(destination));\n\n // we need to initialize the distances\n std::vector<std::vector<int>> distances(\n g.height(), std::vector(g.width(), graph::unreachable()));\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 // now we need to improve the paths as long as possible\n bool improvement_found;\n do {\n // reset the flag at the beginning\n improvement_found = false;\n\n // go through all of the vertices\n for (int y = 0; y < g.height(); ++y) {\n for (int x = 0; x < g.width(); ++x) {\n // skip the cells we cannot reach\n if (distances[y][x] == graph::unreachable()) {\n continue;\n }\n\n // go through the neighbours\n auto u = std::make_pair(x, y);\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\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 improvement_found = true;\n }\n }\n }\n }\n } while (improvement_found);\n\n return distances[destination.second][destination.first];\n}\n"})}),"\n",(0,t.jsxs)(s.admonition,{title:"Relaxation",type:"info",children:[(0,t.jsxs)(s.p,{children:["I have made a brief mention of the relaxation in the comment in the code. You've\nbeen probably taught that ",(0,t.jsx)(s.strong,{children:"relaxation of an edge"})," means that you found\na better solution to the problem."]}),(0,t.jsxs)(s.p,{children:["In general it is an approximation technique that ",(0,t.jsx)(s.em,{children:"reduces"})," the problem of\nfinding the path ",(0,t.jsx)(s.code,{children:"u \u2192 x1 \u2192 \u2026 \u2192 xn \u2192 v"})," to subproblems\n",(0,t.jsx)(s.code,{children:"u \u2192 x1, x1 \u2192 x2, \u2026, xn \u2192 v"})," such that the sum of the costs of each step is\n",(0,t.jsx)(s.strong,{children:"minimal"}),"."]})]}),"\n",(0,t.jsx)(s.h3,{id:"correctness",children:"Correctness"}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.em,{children:"Is our solution correct?"})," It appears to be correct\u2026 We have rather complicated\nmap and our algorithm has finished in an instant with the following output:"]}),"\n",(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{children:"Normal cost: 1\nVortex cost: 5\nGraph:\n#############\n#..#..*.*.**#\n##***.....**#\n#..########.#\n#...###...#.#\n#..#...##.#.#\n#..#.*.#..#.#\n#D...#....#.#\n########*.*.#\n#S..........#\n#############\nCost: 22\n"})}),"\n",(0,t.jsxs)(s.p,{children:["If you have a better look at the map, you will realize that the cost ",(0,t.jsx)(s.code,{children:"22"})," is the\none path skipping the ",(0,t.jsx)(s.code,{children:"*"})," cells, since they cost more than going around."]}),"\n",(0,t.jsxs)(s.p,{children:["We can play around a bit with it. The ",(0,t.jsx)(s.code,{children:"*"})," cells can even be vortices that pull\nyou in with a negative price and let you ",(0,t.jsx)(s.em,{children:"propel"})," yourself out ","\ud83d\ude09"," Let's\nchange their cost to ",(0,t.jsx)(s.code,{children:"-1"})," then and see what's the fastest path to our goal."]}),"\n",(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{children:"Normal cost: 1\nVortex cost: -1\nGraph:\n#############\n#..#..*.*.**#\n##***.....**#\n#..########.#\n#...###...#.#\n#..#...##.#.#\n#..#.*.#..#.#\n#D...#....#.#\n########*.*.#\n#S..........#\n#############\n"})}),"\n",(0,t.jsxs)(s.p,{children:["And we're somehow stuck\u2026 The issue comes from the fact that ",(0,t.jsx)(s.em,{children:"spinning around"})," in\nthe vortices allows us to lower the cost infinitely. That's why after each\niteration there is still a possibility to lower the cost, hence the algorithm\ndoesn't finish. ",(0,t.jsx)(s.em,{children:"What can we do about this?"})]}),"\n",(0,t.jsx)(s.admonition,{type:"tip",children:(0,t.jsx)(s.p,{children:"This algorithm is correct as long as there are no negative loops, i.e. ways how\nto lower the cost infinitely. Therefore we can also just lay a precondition that\nrequires no negative loops to be present."})}),"\n",(0,t.jsx)(s.h3,{id:"fixing-the-infinite-loop",children:"Fixing the infinite loop"}),"\n",(0,t.jsxs)(s.p,{children:["Our issue lies in the fact that we can endlessly lower the cost. Such thing must\nsurely happen in some kind of a loop. We could probably track the relaxations\nand once we spot repeating patterns, we know we can safely terminate with ",(0,t.jsx)(s.em,{children:"some"}),"\nresults at least."]}),"\n",(0,t.jsxs)(s.p,{children:["This approach will not even work on our 2D map, let alone any graph. Problem is\nthat the ",(0,t.jsx)(s.em,{children:"negative loops"})," lower the cost in ",(0,t.jsx)(s.strong,{children:"each"})," iteration and that results\nin lowering of the costs to the cells that are reachable from the said loops.\nThat's why this problem is relatively hard to tackle, it's not that easy to spot\nthe repeating patterns algorithmically."]}),"\n",(0,t.jsx)(s.p,{children:"On the other hand, we can approach this from the different perspective. Let's\nassume the worst-case scenario (generalized for any graph):"}),"\n",(0,t.jsxs)(s.blockquote,{children:["\n",(0,t.jsxs)(s.p,{children:["Let ",(0,t.jsxs)(s.span,{className:"katex",children:[(0,t.jsx)(s.span,{className:"katex-mathml",children:(0,t.jsx)(s.math,{xmlns:"http://www.w3.org/1998/Math/MathML",children:(0,t.jsxs)(s.semantics,{children:[(0,t.jsx)(s.mrow,{children:(0,t.jsxs)(s.msub,{children:[(0,t.jsx)(s.mi,{children:"K"}),(0,t.jsx)(s.mi,{children:"n"})]})}),(0,t.jsx)(s.annotation,{encoding:"application/x-tex",children:"K_n"})]})})}),(0,t.jsx)(s.span,{className:"katex-html","aria-hidden":"true",children:(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"0.8333em",verticalAlign:"-0.15em"}}),(0,t.jsxs)(s.span,{className:"mord",children:[(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.07153em"},children:"K"}),(0,t.jsx)(s.span,{className:"msupsub",children:(0,t.jsxs)(s.span,{className:"vlist-t vlist-t2",children:[(0,t.jsxs)(s.span,{className:"vlist-r",children:[(0,t.jsx)(s.span,{className:"vlist",style:{height:"0.1514em"},children:(0,t.jsxs)(s.span,{style:{top:"-2.55em",marginLeft:"-0.0715em",marginRight:"0.05em"},children:[(0,t.jsx)(s.span,{className:"pstrut",style:{height:"2.7em"}}),(0,t.jsx)(s.span,{className:"sizing reset-size6 size3 mtight",children:(0,t.jsx)(s.span,{className:"mord mathnormal mtight",children:"n"})})]})}),(0,t.jsx)(s.span,{className:"vlist-s",children:"\u200b"})]}),(0,t.jsx)(s.span,{className:"vlist-r",children:(0,t.jsx)(s.span,{className:"vlist",style:{height:"0.15em"},children:(0,t.jsx)(s.span,{})})})]})})]})]})})]})," be complete graph. Let ",(0,t.jsxs)(s.span,{className:"katex",children:[(0,t.jsx)(s.span,{className:"katex-mathml",children:(0,t.jsx)(s.math,{xmlns:"http://www.w3.org/1998/Math/MathML",children:(0,t.jsxs)(s.semantics,{children:[(0,t.jsx)(s.mrow,{children:(0,t.jsx)(s.mi,{children:"P"})}),(0,t.jsx)(s.annotation,{encoding:"application/x-tex",children:"P"})]})})}),(0,t.jsx)(s.span,{className:"katex-html","aria-hidden":"true",children:(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"0.6833em"}}),(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.13889em"},children:"P"})]})})]})," be the shortest path from ",(0,t.jsxs)(s.span,{className:"katex",children:[(0,t.jsx)(s.span,{className:"katex-mathml",children:(0,t.jsx)(s.math,{xmlns:"http://www.w3.org/1998/Math/MathML",children:(0,t.jsxs)(s.semantics,{children:[(0,t.jsx)(s.mrow,{children:(0,t.jsxs)(s.msub,{children:[(0,t.jsx)(s.mi,{children:"v"}),(0,t.jsx)(s.mn,{children:"1"})]})}),(0,t.jsx)(s.annotation,{encoding:"application/x-tex",children:"v_1"})]})})}),(0,t.jsx)(s.span,{className:"katex-html","aria-hidden":"true",children:(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"0.5806em",verticalAlign:"-0.15em"}}),(0,t.jsxs)(s.span,{className:"mord",children:[(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.03588em"},children:"v"}),(0,t.jsx)(s.span,{className:"msupsub",children:(0,t.jsxs)(s.span,{className:"vlist-t vlist-t2",children:[(0,t.jsxs)(s.span,{className:"vlist-r",children:[(0,t.jsx)(s.span,{className:"vlist",style:{height:"0.3011em"},children:(0,t.jsxs)(s.span,{style:{top:"-2.55em",marginLeft:"-0.0359em",marginRight:"0.05em"},children:[(0,t.jsx)(s.span,{className:"pstrut",style:{height:"2.7em"}}),(0,t.jsx)(s.span,{className:"sizing reset-size6 size3 mtight",children:(0,t.jsx)(s.span,{className:"mord mtight",children:"1"})})]})}),(0,t.jsx)(s.span,{className:"vlist-s",children:"\u200b"})]}),(0,t.jsx)(s.span,{className:"vlist-r",children:(0,t.jsx)(s.span,{className:"vlist",style:{height:"0.15em"},children:(0,t.jsx)(s.span,{})})})]})})]})]})})]})," to ",(0,t.jsxs)(s.span,{className:"katex",children:[(0,t.jsx)(s.span,{className:"katex-mathml",children:(0,t.jsx)(s.math,{xmlns:"http://www.w3.org/1998/Math/MathML",children:(0,t.jsxs)(s.semantics,{children:[(0,t.jsx)(s.mrow,{children:(0,t.jsxs)(s.msub,{children:[(0,t.jsx)(s.mi,{children:"v"}),(0,t.jsx)(s.mi,{children:"n"})]})}),(0,t.jsx)(s.annotation,{encoding:"application/x-tex",children:"v_n"})]})})}),(0,t.jsx)(s.span,{className:"katex-html","aria-hidden":"true",children:(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"0.5806em",verticalAlign:"-0.15em"}}),(0,t.jsxs)(s.span,{className:"mord",children:[(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.03588em"},children:"v"}),(0,t.jsx)(s.span,{className:"msupsub",children:(0,t.jsxs)(s.span,{className:"vlist-t vlist-t2",children:[(0,t.jsxs)(s.span,{className:"vlist-r",children:[(0,t.jsx)(s.span,{className:"vlist",style:{height:"0.1514em"},children:(0,t.jsxs)(s.span,{style:{top:"-2.55em",marginLeft:"-0.0359em",marginRight:"0.05em"},children:[(0,t.jsx)(s.span,{className:"pstrut",style:{height:"2.7em"}}),(0,t.jsx)(s.span,{className:"sizing reset-size6 size3 mtight",children:(0,t.jsx)(s.span,{className:"mord mathnormal mtight",children:"n"})})]})}),(0,t.jsx)(s.span,{className:"vlist-s",children:"\u200b"})]}),(0,t.jsx)(s.span,{className:"vlist-r",children:(0,t.jsx)(s.span,{className:"vlist",style:{height:"0.15em"},children:(0,t.jsx)(s.span,{})})})]})})]})]})})]}),"\nsuch that ",(0,t.jsxs)(s.span,{className:"katex",children:[(0,t.jsx)(s.span,{className:"katex-mathml",children:(0,t.jsx)(s.math,{xmlns:"http://www.w3.org/1998/Math/MathML",children:(0,t.jsxs)(s.semantics,{children:[(0,t.jsx)(s.mrow,{children:(0,t.jsx)(s.mi,{children:"P"})}),(0,t.jsx)(s.annotation,{encoding:"application/x-tex",children:"P"})]})})}),(0,t.jsx)(s.span,{className:"katex-html","aria-hidden":"true",children:(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"0.6833em"}}),(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.13889em"},children:"P"})]})})]})," has ",(0,t.jsxs)(s.span,{className:"katex",children:[(0,t.jsx)(s.span,{className:"katex-mathml",children:(0,t.jsx)(s.math,{xmlns:"http://www.w3.org/1998/Math/MathML",children:(0,t.jsxs)(s.semantics,{children:[(0,t.jsxs)(s.mrow,{children:[(0,t.jsx)(s.mi,{children:"n"}),(0,t.jsx)(s.mo,{children:"\u2212"}),(0,t.jsx)(s.mn,{children:"1"})]}),(0,t.jsx)(s.annotation,{encoding:"application/x-tex",children:"n - 1"})]})})}),(0,t.jsxs)(s.span,{className:"katex-html","aria-hidden":"true",children:[(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"0.6667em",verticalAlign:"-0.0833em"}}),(0,t.jsx)(s.span,{className:"mord mathnormal",children:"n"}),(0,t.jsx)(s.span,{className:"mspace",style:{marginRight:"0.2222em"}}),(0,t.jsx)(s.span,{className:"mbin",children:"\u2212"}),(0,t.jsx)(s.span,{className:"mspace",style:{marginRight:"0.2222em"}})]}),(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"0.6444em"}}),(0,t.jsx)(s.span,{className:"mord",children:"1"})]})]})]})," edges, i.e. the shortest path between the two chosen\nvertices visits all vertices (not necessarily in order) and has the lowest\ncost."]}),"\n",(0,t.jsxs)(s.p,{children:["In such scenario assume the worst-case ordering of the relaxations (only one\n",(0,t.jsx)(s.em,{children:"helpful"})," relaxation per iteration). In this case, in each iteration we find\nthe next edge on our path ",(0,t.jsxs)(s.span,{className:"katex",children:[(0,t.jsx)(s.span,{className:"katex-mathml",children:(0,t.jsx)(s.math,{xmlns:"http://www.w3.org/1998/Math/MathML",children:(0,t.jsxs)(s.semantics,{children:[(0,t.jsx)(s.mrow,{children:(0,t.jsx)(s.mi,{children:"P"})}),(0,t.jsx)(s.annotation,{encoding:"application/x-tex",children:"P"})]})})}),(0,t.jsx)(s.span,{className:"katex-html","aria-hidden":"true",children:(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"0.6833em"}}),(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.13889em"},children:"P"})]})})]})," as the last. This means that we need\n",(0,t.jsxs)(s.span,{className:"katex",children:[(0,t.jsx)(s.span,{className:"katex-mathml",children:(0,t.jsx)(s.math,{xmlns:"http://www.w3.org/1998/Math/MathML",children:(0,t.jsxs)(s.semantics,{children:[(0,t.jsxs)(s.mrow,{children:[(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u2223"}),(0,t.jsx)(s.mi,{children:"V"}),(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u2223"}),(0,t.jsx)(s.mo,{children:"\u2212"}),(0,t.jsx)(s.mn,{children:"1"})]}),(0,t.jsx)(s.annotation,{encoding:"application/x-tex",children:"\\vert V \\vert - 1"})]})})}),(0,t.jsxs)(s.span,{className:"katex-html","aria-hidden":"true",children:[(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,t.jsx)(s.span,{className:"mord",children:"\u2223"}),(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.22222em"},children:"V"}),(0,t.jsx)(s.span,{className:"mord",children:"\u2223"}),(0,t.jsx)(s.span,{className:"mspace",style:{marginRight:"0.2222em"}}),(0,t.jsx)(s.span,{className:"mbin",children:"\u2212"}),(0,t.jsx)(s.span,{className:"mspace",style:{marginRight:"0.2222em"}})]}),(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"0.6444em"}}),(0,t.jsx)(s.span,{className:"mord",children:"1"})]})]})]})," iterations to find the shortest path ",(0,t.jsxs)(s.span,{className:"katex",children:[(0,t.jsx)(s.span,{className:"katex-mathml",children:(0,t.jsx)(s.math,{xmlns:"http://www.w3.org/1998/Math/MathML",children:(0,t.jsxs)(s.semantics,{children:[(0,t.jsx)(s.mrow,{children:(0,t.jsx)(s.mi,{children:"P"})}),(0,t.jsx)(s.annotation,{encoding:"application/x-tex",children:"P"})]})})}),(0,t.jsx)(s.span,{className:"katex-html","aria-hidden":"true",children:(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"0.6833em"}}),(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.13889em"},children:"P"})]})})]}),"."]}),"\n",(0,t.jsxs)(s.p,{children:["Because we have laid ",(0,t.jsxs)(s.span,{className:"katex",children:[(0,t.jsx)(s.span,{className:"katex-mathml",children:(0,t.jsx)(s.math,{xmlns:"http://www.w3.org/1998/Math/MathML",children:(0,t.jsxs)(s.semantics,{children:[(0,t.jsx)(s.mrow,{children:(0,t.jsx)(s.mi,{children:"P"})}),(0,t.jsx)(s.annotation,{encoding:"application/x-tex",children:"P"})]})})}),(0,t.jsx)(s.span,{className:"katex-html","aria-hidden":"true",children:(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"0.6833em"}}),(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.13889em"},children:"P"})]})})]})," as the shortest path from ",(0,t.jsxs)(s.span,{className:"katex",children:[(0,t.jsx)(s.span,{className:"katex-mathml",children:(0,t.jsx)(s.math,{xmlns:"http://www.w3.org/1998/Math/MathML",children:(0,t.jsxs)(s.semantics,{children:[(0,t.jsx)(s.mrow,{children:(0,t.jsxs)(s.msub,{children:[(0,t.jsx)(s.mi,{children:"v"}),(0,t.jsx)(s.mn,{children:"1"})]})}),(0,t.jsx)(s.annotation,{encoding:"application/x-tex",children:"v_1"})]})})}),(0,t.jsx)(s.span,{className:"katex-html","aria-hidden":"true",children:(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"0.5806em",verticalAlign:"-0.15em"}}),(0,t.jsxs)(s.span,{className:"mord",children:[(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.03588em"},children:"v"}),(0,t.jsx)(s.span,{className:"msupsub",children:(0,t.jsxs)(s.span,{className:"vlist-t vlist-t2",children:[(0,t.jsxs)(s.span,{className:"vlist-r",children:[(0,t.jsx)(s.span,{className:"vlist",style:{height:"0.3011em"},children:(0,t.jsxs)(s.span,{style:{top:"-2.55em",marginLeft:"-0.0359em",marginRight:"0.05em"},children:[(0,t.jsx)(s.span,{className:"pstrut",style:{height:"2.7em"}}),(0,t.jsx)(s.span,{className:"sizing reset-size6 size3 mtight",children:(0,t.jsx)(s.span,{className:"mord mtight",children:"1"})})]})}),(0,t.jsx)(s.span,{className:"vlist-s",children:"\u200b"})]}),(0,t.jsx)(s.span,{className:"vlist-r",children:(0,t.jsx)(s.span,{className:"vlist",style:{height:"0.15em"},children:(0,t.jsx)(s.span,{})})})]})})]})]})})]})," to ",(0,t.jsxs)(s.span,{className:"katex",children:[(0,t.jsx)(s.span,{className:"katex-mathml",children:(0,t.jsx)(s.math,{xmlns:"http://www.w3.org/1998/Math/MathML",children:(0,t.jsxs)(s.semantics,{children:[(0,t.jsx)(s.mrow,{children:(0,t.jsxs)(s.msub,{children:[(0,t.jsx)(s.mi,{children:"v"}),(0,t.jsx)(s.mi,{children:"n"})]})}),(0,t.jsx)(s.annotation,{encoding:"application/x-tex",children:"v_n"})]})})}),(0,t.jsx)(s.span,{className:"katex-html","aria-hidden":"true",children:(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"0.5806em",verticalAlign:"-0.15em"}}),(0,t.jsxs)(s.span,{className:"mord",children:[(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.03588em"},children:"v"}),(0,t.jsx)(s.span,{className:"msupsub",children:(0,t.jsxs)(s.span,{className:"vlist-t vlist-t2",children:[(0,t.jsxs)(s.span,{className:"vlist-r",children:[(0,t.jsx)(s.span,{className:"vlist",style:{height:"0.1514em"},children:(0,t.jsxs)(s.span,{style:{top:"-2.55em",marginLeft:"-0.0359em",marginRight:"0.05em"},children:[(0,t.jsx)(s.span,{className:"pstrut",style:{height:"2.7em"}}),(0,t.jsx)(s.span,{className:"sizing reset-size6 size3 mtight",children:(0,t.jsx)(s.span,{className:"mord mathnormal mtight",children:"n"})})]})}),(0,t.jsx)(s.span,{className:"vlist-s",children:"\u200b"})]}),(0,t.jsx)(s.span,{className:"vlist-r",children:(0,t.jsx)(s.span,{className:"vlist",style:{height:"0.15em"},children:(0,t.jsx)(s.span,{})})})]})})]})]})})]})," and it\nvisits all vertices, its prefixes are the shortest paths from ",(0,t.jsxs)(s.span,{className:"katex",children:[(0,t.jsx)(s.span,{className:"katex-mathml",children:(0,t.jsx)(s.math,{xmlns:"http://www.w3.org/1998/Math/MathML",children:(0,t.jsxs)(s.semantics,{children:[(0,t.jsx)(s.mrow,{children:(0,t.jsxs)(s.msub,{children:[(0,t.jsx)(s.mi,{children:"v"}),(0,t.jsx)(s.mn,{children:"1"})]})}),(0,t.jsx)(s.annotation,{encoding:"application/x-tex",children:"v_1"})]})})}),(0,t.jsx)(s.span,{className:"katex-html","aria-hidden":"true",children:(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"0.5806em",verticalAlign:"-0.15em"}}),(0,t.jsxs)(s.span,{className:"mord",children:[(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.03588em"},children:"v"}),(0,t.jsx)(s.span,{className:"msupsub",children:(0,t.jsxs)(s.span,{className:"vlist-t vlist-t2",children:[(0,t.jsxs)(s.span,{className:"vlist-r",children:[(0,t.jsx)(s.span,{className:"vlist",style:{height:"0.3011em"},children:(0,t.jsxs)(s.span,{style:{top:"-2.55em",marginLeft:"-0.0359em",marginRight:"0.05em"},children:[(0,t.jsx)(s.span,{className:"pstrut",style:{height:"2.7em"}}),(0,t.jsx)(s.span,{className:"sizing reset-size6 size3 mtight",children:(0,t.jsx)(s.span,{className:"mord mtight",children:"1"})})]})}),(0,t.jsx)(s.span,{className:"vlist-s",children:"\u200b"})]}),(0,t.jsx)(s.span,{className:"vlist-r",children:(0,t.jsx)(s.span,{className:"vlist",style:{height:"0.15em"},children:(0,t.jsx)(s.span,{})})})]})})]})]})})]})," to any\nother vertex in our graph."]}),"\n",(0,t.jsxs)(s.p,{children:["Therefore, we can safely assume that any relaxation after ",(0,t.jsxs)(s.span,{className:"katex",children:[(0,t.jsx)(s.span,{className:"katex-mathml",children:(0,t.jsx)(s.math,{xmlns:"http://www.w3.org/1998/Math/MathML",children:(0,t.jsxs)(s.semantics,{children:[(0,t.jsxs)(s.mrow,{children:[(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u2223"}),(0,t.jsx)(s.mi,{children:"V"}),(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u2223"}),(0,t.jsx)(s.mo,{children:"\u2212"}),(0,t.jsx)(s.mn,{children:"1"})]}),(0,t.jsx)(s.annotation,{encoding:"application/x-tex",children:"\\vert V \\vert - 1"})]})})}),(0,t.jsxs)(s.span,{className:"katex-html","aria-hidden":"true",children:[(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,t.jsx)(s.span,{className:"mord",children:"\u2223"}),(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.22222em"},children:"V"}),(0,t.jsx)(s.span,{className:"mord",children:"\u2223"}),(0,t.jsx)(s.span,{className:"mspace",style:{marginRight:"0.2222em"}}),(0,t.jsx)(s.span,{className:"mbin",children:"\u2212"}),(0,t.jsx)(s.span,{className:"mspace",style:{marginRight:"0.2222em"}})]}),(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"0.6444em"}}),(0,t.jsx)(s.span,{className:"mord",children:"1"})]})]})]}),"\niterations, is the effect of a negative loop in the graph."]}),"\n"]}),"\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.em,{children:"How can we leverage this?"})," We will go through the edges only as many times as\ncells we have. Let's adjust the code to fix the looping:"]}),"\n",(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-cpp",children:"auto bf_finite(const graph& g, const vertex_t& source,\n const vertex_t& destination) -> int {\n // \u2039source\u203a must be within the bounds\n assert(g.has(source));\n\n // \u2039destination\u203a must be within the bounds\n assert(g.has(destination));\n\n // we need to initialize the distances\n std::vector<std::vector<int>> distances(\n g.height(), std::vector(g.width(), graph::unreachable()));\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 // now we only iterate as many times as cells that we have\n for (int i = g.height() * g.width(); i > 0; --i) {\n // go through all of the vertices\n for (int y = 0; y < g.height(); ++y) {\n for (int x = 0; x < g.width(); ++x) {\n // skip the cells we cannot reach\n if (distances[y][x] == graph::unreachable()) {\n continue;\n }\n\n // go through the neighbours\n auto u = std::make_pair(x, y);\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\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 }\n }\n }\n }\n }\n\n return distances[destination.second][destination.first];\n}\n"})}),"\n",(0,t.jsx)(s.p,{children:"And we get the following result:"}),"\n",(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{children:"Normal cost: 1\nVortex cost: -1\nGraph:\n#############\n#..#..*.*.**#\n##***.....**#\n#..########.#\n#...###...#.#\n#..#...##.#.#\n#..#.*.#..#.#\n#D...#....#.#\n########*.*.#\n#S..........#\n#############\nCost: -236\n"})}),"\n",(0,t.jsxs)(s.p,{children:["The negative cost means that there is a way to ",(0,t.jsx)(s.em,{children:"propel"})," ourselves via some\nvortices. Let's adjust the cost of ",(0,t.jsx)(s.em,{children:"vortices"})," back to the original ",(0,t.jsx)(s.code,{children:"5"})," and check\nwhether our modified algorithm works as it did before. And it surely does yield\nthe ",(0,t.jsx)(s.code,{children:"22"})," as before."]}),"\n",(0,t.jsxs)(s.admonition,{title:"Refactoring",type:"tip",children:[(0,t.jsxs)(s.p,{children:["You can definitely notice some ",(0,t.jsx)(s.em,{children:"deep nesting"})," in our code, to counter this\nphenomenon I will convert the looping over ",(0,t.jsx)(s.code,{children:"x"})," and ",(0,t.jsx)(s.code,{children:"y"})," to one variable that can\nbe decomposed to ",(0,t.jsx)(s.code,{children:"x"})," and ",(0,t.jsx)(s.code,{children:"y"}),". It is a very common practice when working with 2D\narrays/lists to represent them as 1D. In our case:"]}),(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{children:"i : 0 \u2192 width * height - 1\nx = i % width\ny = i / width\n"})})]}),"\n",(0,t.jsx)(s.h2,{id:"bellman-ford",children:"Bellman-Ford"}),"\n",(0,t.jsxs)(s.p,{children:["If you have ever attended any Algorithms course that had path-finding in its\nsyllabus, you probably feel like you've seen the algorithm above before",(0,t.jsx)(s.sup,{children:(0,t.jsx)(s.a,{href:"#user-content-fn-3",id:"user-content-fnref-3","data-footnote-ref":!0,"aria-describedby":"footnote-label",children:"3"})}),"\u2026 And\nyes, the first algorithm I have proposed is a very dumb version of the\n",(0,t.jsx)(s.em,{children:"Bellman-Ford"})," algorithm, it's dumb, because it loops ","\ud83d\ude09"," After our \u201clooping\u201d\nprevention we got to the point that is almost the ",(0,t.jsx)(s.em,{children:"Bellman-Ford"})," with the one\nexception that it doesn't report whether there are any negative cycles, it just\nends."]}),"\n",(0,t.jsx)(s.p,{children:"Let's have a look at a proper implementation of the Bellman-Ford algorithm:"}),"\n",(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-cpp",children:"auto bellman_ford(const graph& g, const vertex_t& source)\n -> std::vector<std::vector<int>> {\n // \u2039source\u203a must be within the bounds\n assert(g.has(source));\n\n // we need to initialize the distances\n std::vector<std::vector<int>> distances(\n g.height(), std::vector(g.width(), graph::unreachable()));\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 // now we only iterate as many times as cells that we have\n for (int i = g.height() * g.width(); i > 0; --i) {\n // go through all of the vertices\n for (int v = g.height() * g.width() - 1; v >= 0; --v) {\n int y = v / g.width();\n int x = v % g.width();\n\n // skip the cells we cannot reach\n if (distances[y][x] == graph::unreachable()) {\n continue;\n }\n\n // go through the neighbours\n auto u = std::make_pair(x, y);\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\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 }\n }\n }\n }\n\n // now we check for the negative loops\n bool relaxed = false;\n for (int v = g.height() * g.width() - 1; !relaxed && v >= 0; --v) {\n int y = v / g.width();\n int x = v % g.width();\n\n // skip the cells we cannot reach\n if (distances[y][x] == graph::unreachable()) {\n continue;\n }\n\n // go through the neighbours\n auto u = std::make_pair(x, y);\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\n if (cost != graph::unreachable() &&\n distances[y][x] + cost < distances[y + dy][x + dx]) {\n relaxed = true;\n std::cerr << \"Found a negative loop\\n\";\n break;\n }\n }\n }\n\n return distances;\n}\n"})}),"\n",(0,t.jsx)(s.p,{children:"And if we run it with our negative cost of entering vortices:"}),"\n",(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{children:"[Bellman-Ford] Found a negative loop\n[Bellman-Ford] Cost: -240\n"})}),"\n",(0,t.jsx)(s.h3,{id:"on-the-bellman-ford",children:"On the Bellman-Ford"}),"\n",(0,t.jsx)(s.p,{children:"You might be surprised that we have managed to iterate from a brute-force method\nthat mindlessly tries to find a better path until there are no better paths left\nall the way to the Bellman-Ford algorithm."}),"\n",(0,t.jsxs)(s.p,{children:["I always say that Bellman-Ford is a ",(0,t.jsx)(s.em,{children:"smart"})," brute-force. BF is also an algorithm\nthat leverages ",(0,t.jsx)(s.em,{children:"dynamic programming"}),". You might wonder how can it utilize DP if\nit is \u201ctechnically\u201d a brute-force technique. Table with the shortest distances\nis the thing that makes it DP."]}),"\n",(0,t.jsxs)(s.blockquote,{children:["\n",(0,t.jsx)(s.p,{children:"I might not know the shortest path yet, but I do remember all of other paths,\nand I can improve them, if possible."}),"\n"]}),"\n",(0,t.jsxs)(s.p,{children:["That's where the beauty of both ",(0,t.jsx)(s.em,{children:"dynamic programming"})," and ",(0,t.jsx)(s.em,{children:"relaxing"})," gets merged\ntogether and does its magic."]}),"\n",(0,t.jsx)(s.p,{children:"Proof of the correctness of the BF is done via induction to the number of\niterations. I would suggest to try to prove the correctness yourself and\npossibly look it up, if necessary."}),"\n",(0,t.jsx)(s.p,{children:"Also the correctness of the BF relies on the conclusion we've made when fixing\nthe infinite-loop on our na\xefve BF solution."}),"\n",(0,t.jsx)(s.h2,{id:"time-complexity",children:"Time complexity"}),"\n",(0,t.jsx)(s.p,{children:"Let's have a short look at the time complexities of the presented algorithms:"}),"\n",(0,t.jsxs)(s.ol,{children:["\n",(0,t.jsxs)(s.li,{children:["\n",(0,t.jsx)(s.p,{children:"na\xefve approach: given that there are no negative loops, we are bound by the\nworst-case ordering of the relaxations which results in"}),"\n",(0,t.jsx)(s.span,{className:"katex-display",children:(0,t.jsxs)(s.span,{className:"katex",children:[(0,t.jsx)(s.span,{className:"katex-mathml",children:(0,t.jsx)(s.math,{xmlns:"http://www.w3.org/1998/Math/MathML",display:"block",children:(0,t.jsxs)(s.semantics,{children:[(0,t.jsxs)(s.mrow,{children:[(0,t.jsx)(s.mi,{mathvariant:"script",children:"O"}),(0,t.jsx)(s.mo,{stretchy:"false",children:"("}),(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u2223"}),(0,t.jsx)(s.mi,{children:"V"}),(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u2223"}),(0,t.jsx)(s.mo,{children:"\u22c5"}),(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u2223"}),(0,t.jsx)(s.mi,{children:"E"}),(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u2223"}),(0,t.jsx)(s.mo,{stretchy:"false",children:")"})]}),(0,t.jsx)(s.annotation,{encoding:"application/x-tex",children:"\\mathcal{O}(\\vert V \\vert \\cdot \\vert E \\vert)"})]})})}),(0,t.jsxs)(s.span,{className:"katex-html","aria-hidden":"true",children:[(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,t.jsx)(s.span,{className:"mord mathcal",style:{marginRight:"0.02778em"},children:"O"}),(0,t.jsx)(s.span,{className:"mopen",children:"("}),(0,t.jsx)(s.span,{className:"mord",children:"\u2223"}),(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.22222em"},children:"V"}),(0,t.jsx)(s.span,{className:"mord",children:"\u2223"}),(0,t.jsx)(s.span,{className:"mspace",style:{marginRight:"0.2222em"}}),(0,t.jsx)(s.span,{className:"mbin",children:"\u22c5"}),(0,t.jsx)(s.span,{className:"mspace",style:{marginRight:"0.2222em"}})]}),(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,t.jsx)(s.span,{className:"mord",children:"\u2223"}),(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.05764em"},children:"E"}),(0,t.jsx)(s.span,{className:"mord",children:"\u2223"}),(0,t.jsx)(s.span,{className:"mclose",children:")"})]})]})]})}),"\n"]}),"\n",(0,t.jsxs)(s.li,{children:["\n",(0,t.jsxs)(s.p,{children:["our na\xefve approach with the fixed count of iterations instead of the\n",(0,t.jsx)(s.code,{children:"do-while"})," loop results in the same worst-case time complexity:"]}),"\n",(0,t.jsx)(s.span,{className:"katex-display",children:(0,t.jsxs)(s.span,{className:"katex",children:[(0,t.jsx)(s.span,{className:"katex-mathml",children:(0,t.jsx)(s.math,{xmlns:"http://www.w3.org/1998/Math/MathML",display:"block",children:(0,t.jsxs)(s.semantics,{children:[(0,t.jsxs)(s.mrow,{children:[(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u0398"}),(0,t.jsx)(s.mo,{stretchy:"false",children:"("}),(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u2223"}),(0,t.jsx)(s.mi,{children:"V"}),(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u2223"}),(0,t.jsx)(s.mo,{children:"\u22c5"}),(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u2223"}),(0,t.jsx)(s.mi,{children:"E"}),(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u2223"}),(0,t.jsx)(s.mo,{stretchy:"false",children:")"})]}),(0,t.jsx)(s.annotation,{encoding:"application/x-tex",children:"\\Theta(\\vert V \\vert \\cdot \\vert E \\vert)"})]})})}),(0,t.jsxs)(s.span,{className:"katex-html","aria-hidden":"true",children:[(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,t.jsx)(s.span,{className:"mord",children:"\u0398"}),(0,t.jsx)(s.span,{className:"mopen",children:"("}),(0,t.jsx)(s.span,{className:"mord",children:"\u2223"}),(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.22222em"},children:"V"}),(0,t.jsx)(s.span,{className:"mord",children:"\u2223"}),(0,t.jsx)(s.span,{className:"mspace",style:{marginRight:"0.2222em"}}),(0,t.jsx)(s.span,{className:"mbin",children:"\u22c5"}),(0,t.jsx)(s.span,{className:"mspace",style:{marginRight:"0.2222em"}})]}),(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,t.jsx)(s.span,{className:"mord",children:"\u2223"}),(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.05764em"},children:"E"}),(0,t.jsx)(s.span,{className:"mord",children:"\u2223"}),(0,t.jsx)(s.span,{className:"mclose",children:")"})]})]})]})}),"\n"]}),"\n",(0,t.jsxs)(s.li,{children:["\n",(0,t.jsx)(s.p,{children:"and finally the well-known Bellman-Ford's algorithm time complexity:"}),"\n",(0,t.jsx)(s.span,{className:"katex-display",children:(0,t.jsxs)(s.span,{className:"katex",children:[(0,t.jsx)(s.span,{className:"katex-mathml",children:(0,t.jsx)(s.math,{xmlns:"http://www.w3.org/1998/Math/MathML",display:"block",children:(0,t.jsxs)(s.semantics,{children:[(0,t.jsxs)(s.mrow,{children:[(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u0398"}),(0,t.jsx)(s.mo,{stretchy:"false",children:"("}),(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u2223"}),(0,t.jsx)(s.mi,{children:"V"}),(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u2223"}),(0,t.jsx)(s.mo,{children:"\u22c5"}),(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u2223"}),(0,t.jsx)(s.mi,{children:"E"}),(0,t.jsx)(s.mi,{mathvariant:"normal",children:"\u2223"}),(0,t.jsx)(s.mo,{stretchy:"false",children:")"})]}),(0,t.jsx)(s.annotation,{encoding:"application/x-tex",children:"\\Theta(\\vert V \\vert \\cdot \\vert E \\vert)"})]})})}),(0,t.jsxs)(s.span,{className:"katex-html","aria-hidden":"true",children:[(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,t.jsx)(s.span,{className:"mord",children:"\u0398"}),(0,t.jsx)(s.span,{className:"mopen",children:"("}),(0,t.jsx)(s.span,{className:"mord",children:"\u2223"}),(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.22222em"},children:"V"}),(0,t.jsx)(s.span,{className:"mord",children:"\u2223"}),(0,t.jsx)(s.span,{className:"mspace",style:{marginRight:"0.2222em"}}),(0,t.jsx)(s.span,{className:"mbin",children:"\u22c5"}),(0,t.jsx)(s.span,{className:"mspace",style:{marginRight:"0.2222em"}})]}),(0,t.jsxs)(s.span,{className:"base",children:[(0,t.jsx)(s.span,{className:"strut",style:{height:"1em",verticalAlign:"-0.25em"}}),(0,t.jsx)(s.span,{className:"mord",children:"\u2223"}),(0,t.jsx)(s.span,{className:"mord mathnormal",style:{marginRight:"0.05764em"},children:"E"}),(0,t.jsx)(s.span,{className:"mord",children:"\u2223"}),(0,t.jsx)(s.span,{className:"mclose",children:")"})]})]})]})}),"\n"]}),"\n"]}),"\n",(0,t.jsx)(s.h2,{id:"small-refactor",children:"Small refactor"}),"\n",(0,t.jsx)(s.p,{children:"Since we are literally copy-pasting the body of the loops just for the sake of\nrelaxing, we can factor that part out into a separate function:"}),"\n",(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-cpp",children:"static auto _check_vertex(const graph& g,\n std::vector<std::vector<int>>& distances, int v,\n bool check_only = false) -> bool {\n bool improvement_found = false;\n\n // unpack the vertex coordinates\n int y = v / g.width();\n int x = v % g.width();\n\n // skip the cells we cannot reach\n if (distances[y][x] == graph::unreachable()) {\n return false;\n }\n\n // go through the neighbours\n auto u = std::make_pair(x, y);\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\n if (cost != graph::unreachable() &&\n distances[y][x] + cost < distances[y + dy][x + dx]) {\n if (check_only) {\n return true;\n }\n\n distances[y + dy][x + dx] = distances[y][x] + cost;\n improvement_found = true;\n }\n }\n\n return improvement_found;\n}\n"})}),"\n",(0,t.jsxs)(s.p,{children:["This function can be also used for checking the negative loops at the end of the\nBF by using the ",(0,t.jsx)(s.code,{children:"check_only"})," parameter to signal that we just want to know if\nthere would be any edge relaxed instead of performing the relaxation itself."]}),"\n",(0,t.jsx)(s.p,{children:"Then we can also see the differences between the specific versions of our\npath-finding algorithms in a clear way:"}),"\n",(0,t.jsx)(s.pre,{children:(0,t.jsx)(s.code,{className:"language-cpp",children:'auto bf(const graph& g, const vertex_t& source, const vertex_t& destination)\n -> int {\n // \u2039source\u203a must be within the bounds\n assert(g.has(source));\n\n // \u2039destination\u203a must be within the bounds\n assert(g.has(destination));\n\n // we need to initialize the distances\n std::vector<std::vector<int>> distances(\n g.height(), std::vector(g.width(), graph::unreachable()));\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 // now we need to improve the paths as long as possible\n bool improvement_found;\n do {\n // reset the flag at the beginning\n improvement_found = false;\n\n // go through all of the vertices\n for (int v = g.height() * g.width() - 1; v >= 0; --v) {\n improvement_found = _check_vertex(g, distances, v) || improvement_found;\n }\n } while (improvement_found);\n\n return distances[destination.second][destination.first];\n}\n\nauto bf_finite(const graph& g, const vertex_t& source,\n const vertex_t& destination) -> int {\n // \u2039source\u203a must be within the bounds\n assert(g.has(source));\n\n // \u2039destination\u203a must be within the bounds\n assert(g.has(destination));\n\n // we need to initialize the distances\n std::vector<std::vector<int>> distances(\n g.height(), std::vector(g.width(), graph::unreachable()));\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 // now we only iterate as many times as cells that we have\n for (int i = g.height() * g.width(); i > 0; --i) {\n // go through all of the vertices\n for (int v = g.height() * g.width() - 1; v >= 0; --v) {\n _check_vertex(g, distances, v);\n }\n }\n\n return distances[destination.second][destination.first];\n}\n\nauto bellman_ford(const graph& g, const vertex_t& source)\n -> std::vector<std::vector<int>> {\n // \u2039source\u203a must be within the bounds\n assert(g.has(source));\n\n // we need to initialize the distances\n std::vector<std::vector<int>> distances(\n g.height(), std::vector(g.width(), graph::unreachable()));\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 // now we only iterate as many times as cells that we have\n for (int i = g.height() * g.width(); i > 0; --i) {\n // go through all of the vertices\n for (int v = g.height() * g.width() - 1; v >= 0; --v) {\n _check_vertex(g, distances, v);\n }\n }\n\n // now we check for the negative loops\n for (int v = g.height() * g.width() - 1; v >= 0; --v) {\n if (_check_vertex(g, distances, v, true)) {\n std::cerr << "[Bellman-Ford] Found a negative loop\\n";\n break;\n }\n }\n\n return distances;\n}\n\n'})}),"\n",(0,t.jsx)(s.hr,{}),"\n",(0,t.jsx)(s.admonition,{type:"tip",children:(0,t.jsxs)(s.p,{children:["You might've noticed that I've been using abbreviation ",(0,t.jsx)(s.em,{children:"BF"})," interchangeably for\nboth ",(0,t.jsx)(s.em,{children:"Bellman-Ford"})," and ",(0,t.jsx)(s.em,{children:"brute-force"}),". If you think about the way Bellman-Ford\nalgorithm works, you should realize that in the worst case it's updating the\nshortest path till there no shorter path exists, so in a sense, you could really\nconsider it a brute-force algorithm."]})}),"\n",(0,t.jsxs)(s.section,{"data-footnotes":!0,className:"footnotes",children:[(0,t.jsx)(s.h2,{className:"sr-only",id:"footnote-label",children:"Footnotes"}),"\n",(0,t.jsxs)(s.ol,{children:["\n",(0,t.jsxs)(s.li,{id:"user-content-fn-1",children:["\n",(0,t.jsxs)(s.p,{children:[(0,t.jsx)(s.a,{href:"https://en.wikipedia.org/wiki/Breadth-first_search",children:"Breadth-first search"})," ",(0,t.jsx)(s.a,{href:"#user-content-fnref-1","data-footnote-backref":"","aria-label":"Back to reference 1",className:"data-footnote-backref",children:"\u21a9"})]}),"\n"]}),"\n",(0,t.jsxs)(s.li,{id:"user-content-fn-2",children:["\n",(0,t.jsxs)(s.p,{children:["Of course, there are some technicalities like keeping track of the visited\nvertices to not taint the shortest path by already visited vertices. ",(0,t.jsx)(s.a,{href:"#user-content-fnref-2","data-footnote-backref":"","aria-label":"Back to reference 2",className:"data-footnote-backref",children:"\u21a9"})]}),"\n"]}),"\n",(0,t.jsxs)(s.li,{id:"user-content-fn-3",children:["\n",(0,t.jsxs)(s.p,{children:["or at least you should, LOL ",(0,t.jsx)(s.a,{href:"#user-content-fnref-3","data-footnote-backref":"","aria-label":"Back to reference 3",className:"data-footnote-backref",children:"\u21a9"})]}),"\n"]}),"\n"]}),"\n"]})]})}function d(e={}){const{wrapper:s}={...(0,a.a)(),...e.components};return s?(0,t.jsx)(s,{...e,children:(0,t.jsx)(o,{...e})}):o(e)}},11151:(e,s,n)=>{n.d(s,{Z:()=>l,a:()=>r});var t=n(67294);const a={},i=t.createContext(a);function r(e){const s=t.useContext(i);return t.useMemo((function(){return"function"==typeof e?e(s):{...s,...e}}),[s,e])}function l(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(a):e.components||a:r(e.components),t.createElement(i.Provider,{value:s},e.children)}}}]); |