Stanley Wang

musician : developer

Dijkstra's Algorithm

·10 min

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)
Dijkstra Proof
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