Dijkstra's Algorithm

COMP 251 Notes
·10 min·[ Algorithms ]

Quick Recap on Dijkstra's:

  • no negative-weight edges
  • weighted version of breadth-first-search
    • instead of FIFO queue, use priority queue
    • keys are shortest-path weights d[v]d[v]
  • two sets of vertices
    • SS vertices whose final shortest-path weights are already found
    • QQ priority queue = vertices still in the queue, those we still need to evaluate their shortest path
  • greedy-choice: at each step we choose the light edge

Code#

def init_single_source(vertices, source_node):
	for v in vertices:
		d[v] = inf
		p[v] = nil
	d[s] = 0
 
def relax(u, v, w):
	if d[v] > d[u] + w(u, v):
		d[v] = d[u] + w(u, v)
		p[v] = u
 
def dijkstra(V, E, w, s):
	init_single_source(V, s):
	S = set() # init empty
	Q = priority_queue(V)
 
	while Q is not empty:
		u = extract_min(Q)
		S = S.add(u)
		for v in adj_list[u]:
			relax(u, v, w)

Mathematical Context For Path Properties#

First, let's cover the working parts & properties:

Triangle Inequality#

The Triangle Inequality states that for all (u,v)E(u,v)\in E, we have δ(s,v)δ(s,u)+w(u,v)\delta(s,v) \leq \delta(s,u)+w(u,v).

Proof#

  • we have a path suvs\leadsto u\to v, as well as a shortest path svs\leadsto v
    • the weight of the shortest path svs\leadsto v is \leq any path svs\leadsto v
  • let us say that the path su=δ(s,u)s\leadsto u=\delta(s,u)
  • this means if we use the shortest path sus\leadsto u and direct edge uvu\to v, then the weight along suv=δ(s,u)+w(u,v)s\leadsto u\to v=\delta(s,u)+w(u,v)
Triangle Inequality

Upper Bound Property#

The Upper Bound Property states that we always have d[v]δ(s,v),vVd[v]\geq\delta(s,v),\forall v\in V. Once d[v]=δ(s,v)d[v]=\delta(s,v), it never changes.

Proof by Contradiction on Inequality#

  • let's assume this starts initially true
  • then, assume vV;d[v]<δ(s,v)\exists v\in V;d[v]<\delta(s,v)
    • this is the first instance of it happening

We know that this can't have happened at initialization, because init_single_source() sets all d[v]=d[v]=\infty, therefore this must have happened at some point during the algorithm's run time.

Let uu be the vertex that causes d[v]d[v] to change, since in order for us to have altered d[v]d[v], relax(u, v, w) must have been called.

Within relax(u, v, w), d[v]d[v] is altered only if:

  • d[v] > d[u] + w(u,v) evaluates to true.
  • if so, d[v] = d[u] + w(u, v) is the change made to d[v]d[v]

Recall our initial assumption, we have: d[v]<δ(s,v)d[v]<\delta(s,v)

  1. via Triangle Inequality, δ(s,v)δ(s,u)+w(u,v)\delta(s,v)\leq\delta(s,u)+w(u,v)
  2. d(s,u)d[u]d(s,u)\leq d[u], since vv was the first vertex where its estimate was less than the shortest path, meaning:
    • δ(s,u)d[u]    δ(s,u)+w(u,v)d[u]+w(u,v)\delta(s,u)\leq d[u]\implies \delta(s,u)+w(u,v)\leq d[u]+w(u,v)
  3. this results in the full inequality:
d[v]<δ(s,v)δ(s,u)+w(u,v)d[u]+w(u,v)d[v]<\delta(s,v)\leq\delta(s,u)+w(u,v)\leq d[u]+w(u,v)

However, this is impossible, since relax(u, v, w) set d[v]=d[u]+w(u,v)d[v]=d[u]+w(u,v), and nothing can be equal and be explicitly less than something else simultaneously.

Thus, we have proved δ(s,v)d[v],vV\delta(s,v)\leq d[v],\forall v\in V. \blacksquare

No-Path Property#

The No-Path Property states that if δ(s,v)=\delta(s,v)=\infty, then d[v]d[v] will always equal \infty

Proof#

  • via the Upper Bound Property, d[v]δ(s,v)d[v]\geq\delta(s,v)
  • this means δ(s,v)=    d[v]=\delta(s,v)=\infty \implies d[v]=\infty

\blacksquare

Convergence Property#

The Convergence Property states that if:

  1. we have a path suv=δ(s,v)s\leadsto u\to v=\delta(s,v) – (it is a shortest path)
  2. d[u]=δ(s,u)d[u]=\delta(s,u)
  3. we call relax(u, v, w),

then d[v]=δ(s,v)d[v]=\delta(s,v) afterward.

Proof#

We relax vv within this code:

if d[v] > d[u] + w(u, v):
	d[v] = d[u] + w(u, v)
	p[v] = u

After this code, d[v]d[u]+w(u,v)d[v]\leq d[u]+w(u,v), because when entering relax(u, v, w):

  1. if d[v]d[v] was d[u]+w(u,v)\leq d[u]+w(u,v) – we would bypass the if-condition, and nothing happens
  2. if d[v]d[v] was >>, then it is set =d[u]+w(u,v)=d[u]+w(u,v)

The only two cases resulting in d[v]d[u]+w(u,v)d[v]\leq d[u]+w(u,v).

We can take the RHS and simplify it, as we have defined d[u]=δ(s,u)d[u]=\delta(s,u):

d[v]      δ(s,u)+w(u,v)d[v]\leq\;\;\;\delta(s,u)+w(u,v)

Since we defined suvs\leadsto u\to v to be a shortest path, meaning:

d[v]      δ(s,v)=δ(s,u)+w(u,v)d[v]\leq\;\;\;\delta(s,v)=\delta(s,u)+w(u,v)

Finally, by the Upper Bound Property, we know that d[v]δ(s,v)d[v]\geq \delta(s,v). This means we must have d[v]=δ(s,v)d[v]=\delta(s,v). \blacksquare

Path Relaxation Property#

Let p=v0,v1,,vkp = \langle v_{0},v_{1},\dots,v_{k} \rangle be a shortest path from s=v0s=v_{0} to vkv_{k}. Relaxing these edges, in order, will ensure that d[vk]=δ(v0,vk)d[v_{k}]=\delta(v_{0},v_{k}). (The shortest path estimate at vkv_{k} is the correct one).

Proof by Induction#

We will show via induction on the number of vertices that d[vi]=δ(s,vi)d[v_{i}]=\delta(s,v_{i}) after the edge (vi1,vi)(v_{i-1},v_{i}) is relaxed.

Base Case: i=0i=0, and v0=sv_{0}=s.

At initialization in init_single_source(), we set d[s]=0    δ(s,s)=0d[s]=0\implies\delta(s,s)=0.

Inductive Step: Assume d[vi1]=δ(vi1,s)d[v_{i-1}]=\delta(v_{i-1}, s).

As we relax edge (vi1,vi)(v_{i-1},v_{i}), note that we have met the pre-conditions for the Convergence Property:

  1. we have a shortest path svi1vivks\leadsto v_{i-1} \to v_{i} \leadsto v_{k}
    • by optimal substructure, the path svi1vis\leadsto v_{i-1} \to v_{i} must also be a shortest path δ(s,vi)\delta(s,v_{i})
  2. we have d[vi1]=δ(vi1,s)d[v_{i-1}]=\delta(v_{i-1},s)
  3. we are now calling relax on (vi1,vi)(v_{i-1},v_{i})

hence, d[vi]d[v_{i}] converges to be δ(vi,s)\delta(v_{i},s) and never changes.

We have proved by induction that d[vk]=δ(v0,vk)d[v_{k}]=\delta(v_{0},v_{k}) if we relax the edges in order. \blacksquare

Dijkstra's Proof#

via Loop Invariant#

We will prove via a Loop Invariant that Dijkstra's Algorithm is correct.

def dijkstra(V, E, w, s):
	init_single_source(V, s):
	S = set() # init empty
	Q = priority_queue(V)
 
	while Q is not empty:
		u = extract_min(Q)
		S.add(u)
		for v in adj_list[u]:
			relax(u, v, w)

Loop Invariant: At the end of each iteration of the while loop, d[v]=δ(s,v),vSd[v]=\delta(s,v), \forall v\in S

Initialization#

At initialization, SS is an empty set, and so the loop invariant holds as a by-product of having no vSv\in S yet.

Maintenance#

Show that d[v]=δ(s,v)d[v] = \delta(s,v) when vv is added to SS in each iteration.

We will prove the maintenance property through contradiction:

Assume that for the first time, after an iteration on some vertex vv, we have added vv to SS, and d[v]δ(s,v)d[v]\neq\delta(s,v).

What do we know?

For starters, we know that vsv\neq s, as d[s]=δ(s,s)=0d[s]=\delta(s,s)=0. This means that sSs\in S and SS\neq \emptyset when vv is added.

We also know, by the No-Path Property, there exists some path svs\leadsto v. Otherwise, the property states that:

{  δ(s,v)=  }    {  d[v] will always =  }    {  d[v]==δ(s,v)  }\{\;\delta(s,v)=\infty\; \} \implies \{ \; d[v] \text{ will always }= \infty \;\} \implies \{ \;d[v]=\infty=\delta(s,v)\;\}

which contradicts our assumption that d[v]δ(s,v)d[v]\neq\delta(s,v).

Since there exists a path svs\leadsto v, there must exist a shortest path, pp, from svs\leadsto v.

Allow us to decompose pp into sp1xyp2vs\leadsto^{p_{1}} x\to y\leadsto^{p_{2}}v, such that {s,x}S\{ s,x \}\in S, {y,v}Q\{ y,v \}\in Q, and edge (x,y)(x,y) is the edge crossing the two sets S,QS,Q.

Claim: d[y]=δ(y,s)d[y]=\delta(y,s) when uu is added to SS

Proof:

  1. by optimal substructure, any subpath within svs\leadsto v, such as sxys\leadsto x\to y, is a shortest path as well
  2. xS    d[x]=δ(s,x)x\in S \implies d[x] = \delta(s,x)
  3. we called relax on edge (x,y)(x,y) at the time of adding xx to SS

This means that if y=vy=v, we have already reached a contradiction, as our initial assumption was:

d[v]δ(s,v)d[v] \neq\delta(s,v)

and we have just proved that the estimate is the correct delta, and the proof is finished.

However, what if yvy\neq v? Can we still reach a contradiction?

Once again, we know that there exists a shortest path pp from svs\leadsto v via the No-Path Property, and that any subpath along pp is also a shortest path by optimal substructure. This implies a chain of logic:

  1. sys\leadsto y is a shortest path
  2. by our Claim, d[y]=δ(s,y)d[y]=\delta(s,y)
  3. since there are no non-negative edge weights, a shortest path syvs\leadsto y\leadsto v must be at least as long as δ(s,y)\delta(s,y), meaning:
  4. syv    δ(s,y)δ(s,v)s\leadsto y\leadsto v\implies\delta(s,y)\leq\delta(s,v)
    • this is because we must pass yy to get to vv
  5. δ(s,v)d[v]\delta(s,v)\leq d[v] by the Upper Bound Property

Putting this all together:

 d[y]=δ(s,y)(2) δ(s,v)(4) d[v](5)     d[y]d[v]\begin{align*} \ d[y]&=\delta(s,y)\quad\text{(2)}\\ \ &\leq \delta(s,v)\quad\text{(4)}\\ \ &\leq d[v]\quad \quad \, \text{(5)}\\ \ &\implies d[y] \leq d[v]\\ \end{align*}

Lastly, we know that:

  • we are in the iteration of the while loop where we choose vv
  • QQ stores a vertex vv as a key-value pair { v : d[v] }
  • extract_min(Q) chooses to extract the vertex v if d[v] is minimum across all estimates it finds in Q
  • both vv and yy were in QQ when we chose vv

This means in order for vv to have been chosen, d[v]d[y]d[v]\leq d[y]. We can conclude:

d[v]d[y]d[y]d[v]    d[v]=d[y]d[v]\leq d[y] \land d[y]\leq d[v]\implies d[v]=d[y]

The estimate d[v]d[v] must be equal to the estimate d[y]d[y] if it is both \leq and \geq d[y]d[y]. This, again, contradicts our initial assumption that d[v]δ(s,v)d[v]\neq\delta(s,v) as d[v]=d[y]d[v]=d[y], and d[y]=δ(s,y)d[y]=\delta(s,y) by our initial Claim. \blacksquare

Termination#

At the end of the while loop, QQ (which was equal to VV) is now the \emptyset. At each iteration, we added the current vertex to SS, meaning that now, S=VS=V. This implies that:

d[v]=δ(s,v),  vVd[v]=\delta(s,v),\;\forall v\in V

The loop variant has been shown to hold across initialization, maintenance, and termination, thus proving Dijkstra's Algorithm. \blacksquare