Korteste Vei II Lars Vidar Magnusson 11.4.2014 Kapittel 24 Bellman-Ford algoritmen Dijkstra algoritmen
Bellman-Ford Algoritmen Bellman-Ford er en single-source korteste vei algoritme. Den tillater negative vekter Den returnerer true hvis ingen negative sykler er tilgjengelig fra startnoden. Den finner v.d og v.π for alle v V.
Pseudokode for Bellman-Ford Algoritmen Pseudokoden for Bellman-Ford algoritmen er listet under.
Bellman-Ford på en Eksempelgraf Figuren under viser hvordan Bellman-Ford algoritmen kjører på en eksempelgraf. Kantene blir avslappet i følgende tilfeldige rekkefølge: (t, x), (t, y), (t, z), (x, t), (y, x), (y, z), (z, x), (z, s), (s, t), (s, y).
Analyse av Kjøretiden til Bellman-Ford algoritmen Bellman-Ford algoritmen er svært enkel å analysere siden vi bare kan telle løkker. Vi har en løkke som kjører V 1 ganger med en nestet løkke som kaller Relax på alle kantene. Den andre løkken er av lavere orden siden den bare løkker gjennom alle kantene. Dette resulterer i en kjøretid på Θ(VE).
Korrektheten til Bellman-Ford Algoritmen Verdier vi får for hvert pass gjennom nodene og hvor for verdiene konvergerer avhenger av rekkefølgen vi vurderer kantene. Algoritmen er garantert å konvergere etter V 1 pass, hvis vi antar at det ikke er negative sykler tilgjengelig. Beviset for dette avhenger av path-relaxation egenskapen. Vi lar v være en node som er tilgjengelig fra s, og p = v 0, v 1,..., v k være en korteste vei fra s til v i.e. s = v 0 og v = v k. Siden p er asyklisk vil antall kanter i p være V 1 i.e. k V 1. Hver iterasjon av den ytre løkken kaller Relax på alle kantene. I verstefall vil første iterasjonen kalle Relax på (v 0, v 1 ), den andre på (v 1, v 2 ) og den kte iterasjonen på (v k 1, v k ). Utifra path-relaxation egenskapen vil v.d = v k.d = δ(s, v k ) = δ(s, v).
Korrektheten til Bellman-Ford Algoritmen II Hva med returverdien til Bellman-Ford algoritmen? Den skal være true hvis ingen negative sykler er tilgjengelig fra s. Vi begynner med å anta at det ikke er noen negative sykler tilgjengelig. Ved terminering vil, for alle (u, v) E, v.d = δ(s, v) δ(s, u) + w(u, v) = u.d + w(u, v) (triangle ulikheten) Så Bellman-Ford algoritmen returnerer true.
Korrektheten til Bellman-Ford Algoritmen II La oss nå se på alternativet hvor det finnes en negativ sykel c = v 0, v 1,..., v k hvor v 0 = v k er tilgjengelig fra s. Vi vet da utifra definisjonen til en negativ sykel at k i=1 (v i 1, v i ) < 0. Vi prøver å finne en selvmotsigelse ved å anta at algoritmen returnerer true alikevel. Da skal vi for alle i = 1, 2,..., k ha at v i.d v i 1.d + w(v i 1, v i ). k v i.d i=1 = 0 k (v i 1.d + w(v i 1, v)) i=1 k v i 1.d + i=1 k w(v i 1, v) i=1 k w(v i 1, v) Dette er en selvmotsigelse i.e. vi har bevist at Bellman-Ford returnerer false når en negativ sykel er tilgjengelig fra s. i=1
Dijkstra Algoritmen Dijkstra algoritmen er også en single-source korteste vei algoritme. I motsetning til Bellman-Ford støtter ikke Dijkstra negative vekter. Algoritmen kan beskrives som en vektet versjon av breddeførst søk. I stedet for en FIFO kø bruker Dijkstra en prioritetskø Sorteringsnøkkelen er korteste vei estimatet v. d. Vi har to sett med noder: S er settet med noder hvor v.d = δ(s, v). Q er settet med noder som ligger i prioritetskøen i.e V S.
Pseudokode for Dijkstra Algoritmen Pseudokode for Dijkstra algoritmen er listet under.
Mer om Dijkstra Algoritmen Dijkstra algoritmen ligner veldig på Prim algoritmen for minimum spenntre. Beregner v. d som benyttes som sorteringsnøkkel. Dijkstra algoritmen kan ses som en grådig algoritme, siden den alltid velger den letteste (nærmeste) noden i V S for å legge til S.
Hvordan Dijkstra Kjører på en Eksempelgraf Figuren under viser hvordan Dijkstra algoritmen kjører på en eksempelgraf.
Korrektheten til Dijkstra Algoritmen Vi tar i bruk en loop invariant for å bevise at Dijkstra algoritmen er korrekt. Loop invariant: Ved starten av hver iterasjon av while-løkka er v.d = δ(s, v) for alle v S. Initialisering: Ved starten er S =, så invarianten er trivielt oppfylt. Vi tar med terimineringsteget her, da det er vedlikeholdsteget som tar plass. Merk at vi hopper i resonnementet. Terminering: Ved slutten av kjøring er Q =. Dette impliserer S = V og at v.d = δ(s, v) for alle v V
Korrektheten til Dijkstra Algoritmen II Da gjenstår det å se på vedlikeholdsteget. Vedlikehold: Vi må vise at u.d = δ(s, u) når u blir lagt til S for hver iterasjon. Vi antar at det eksisterer en u slik at u.d δ(s, u). Vi antar også, uten tap av generalitet, at u er den første noden hvor dette inntreffer. Utifra dette kan vi gjøre følgende observasjoner: u s, siden s.d = δ(s, s) = 0. Vi har derfor s S. Vi må ha en sti s u, siden u.d = δ(s, u) = hvis det motsatt var tilfellet.
Korrektheten til Dijkstra Algoritmen II Så vi har en sti s u. Da må det nødvendigvis også eksistere en korteste vei p sti s u. Rett før u blir lagt til S kobler stien p en node i S (s) til en node i V S (u). Vi lar y være første node på stien p som er i V S. x er forgjengeren til y i S. Vi kan dele opp p i s p1 x y p2 u Vi kunne hatt s = x og u = y slik at p 1 og p 2 har ingen kanter. Figuren på neste slide illustrerer den tenkte situasjonen.
Korrektheten til Dijkstra Algoritmen III Figuren under viser den tenkte situasjonen beskrevet på forrige slide.
Korrektheten til Dijkstra Algoritmen IV Vi vet at y.d = δ(s, y) når u legges til S utifra den tenkte situasjonen hvor u er den første noden hvor dette ikke stemmer, og konvergensegenskapen. y er på den korteste vei s u og alle vektene på kantene er ikke negative i.e. δ(s, y) δ(s, u). Så vi får: y.d = δ(s, y) δ(s, u) u. d (øvre grense) Men vi har samtidig at u.d y.d siden u velges før y. Så samlet blir disse ulikhetene til likheter, og vi har oppnådd en selvmotsigelse med antagelsen at u.d δ(s, u). Vi har med dette bevist at Dijkstra algoritmen er korrekt.
Analyse av Kjøretiden til Dijsktra Algoritmen Som med Prim algoritmen, er kjøretiden til Dijkstra algoritmen avhengig av hva slags prioritetskø som brukes. Hvis vi hadde tatt i bruk en prioritetskø implementer på en binær heap ville vi få Hver operasjon tar O(log V ) Vi har V Extract-Min operasjoner og E Decrease-Key operasjoner. Totalt sett får vi da en kjøretid på O((V + E) log V ) Dette blir til O(E log V ) hvis alle nodene er sammenkoblet. Hvis vi i stedet hadde brukt en Fibonacci heap kunne vi fått en kjøretid på O(V log V + E).