CMD + K

Algoritmer og datastrukturerKorteste vei fra én kildeBegreper & formler12
6 min lesing1 video

Korteste vei fra én kilde

Vi søker korteste vei fra én startnode til alle andre i en vektet, rettet graf. Kjernen er relax-operasjonen som gradvis strammer inn avstandsestimatene. Bellman-Ford takler negative kanter og oppdager negative sykler; Dijkstra er raskere, men forutsetter ikke-negative vekter.

Læringsmål
  • 01Forklare relax-operasjonen og hvorfor avstandsestimatet alltid er et øvre tak som synker mot riktig verdi
  • 02Begrunne hvorfor Bellman-Ford trenger V-1 runder og hvordan en ekstra runde avslører en negativ sykel
  • 03Forklare hvorfor Dijkstra krever ikke-negative kantvekter og hva som går galt ellers
  • 04Velge mellom DAG-korteste-vei, Bellman-Ford og Dijkstra ut fra om grafen er asyklisk og om vektene kan være negative

Én kilde, alle mål

Et navigasjonssystem som regner ut raskeste rute hjem løser et korteste-vei-problem. Vi har en vektet, rettet graf — kryssene er noder, veistrekkene er kanter, og vekten er kjøretid — og vi vil finne korteste vei fra én startnode til hver eneste andre node. Vekten på en sti er summen av kantvektene, og vi skriver for den minste mulige stivekten fra til .

Hvorfor regne ut til alle noder når vi kanskje bare vil ett sted? Fordi det viser seg at det ikke er billigere å finne korteste vei til ett enkelt mål enn til alle. Algoritmene bygger uansett opp avstandene lag for lag utover fra kilden, og målet ditt ligger et eller annet sted i det laget. Resultatet er ikke bare avstandene, men et helt korteste-vei-tre: fra hver node peker en forgjengerpeker bakover mot kilden, slik at selve veien kan leses ut ved å følge pekerne hjem.

105392162S:0A:8C:5B:9D:7E:9d-verdier i nodene; grønne kanter = korteste-vei-tre
FIGKorteste-vei-tre med d-verdier

I figuren står hver node merket med sin korteste avstand fra S. De grønne kantene er treet av forgjengerpekere. Merk at korteste vei til A ikke går direkte S→A (vekt 10), men omveien S→C→A med samlet vekt 5+3 = 8. Det er hele utfordringen: en kortere vei kan gå via en omvei.

Relax: den ene operasjonen alt hviler på

Alle algoritmene i dette kapittelet er bygd av den samme byggeklossen, relax. Vi holder for hver node et estimat på avstanden, satt til i starten og for kilden. Estimatet er alltid et øvre tak på den sanne avstanden , og det synker monotont mot riktig verdi etter hvert som vi oppdager kortere veier.

Å relakse kanten fra til betyr: sjekk om veien til , etterfulgt av kanten , er bedre enn dagens estimat for . Hvis den er det, stram inn. ƒrelax

RELAX(u, v):
  hvis d[u] + vekt(u, v) < d[v]:
      d[v] = d[u] + vekt(u, v)
      forelder[v] = u

Korrektheten hviler på trekantulikheten: den korteste avstanden til er aldri lengre enn avstanden til pluss kanten fra til . ƒtrekantulikheten for korteste vei Forskjellen mellom algoritmene er bare i hvilken rekkefølge de relakser kantene. Det er det hele.

Bellman-Ford: brutalt, men takler negative kanter

Den enkleste strategien er å ikke være smart i det hele tatt: relaks alle kantene, om og om igjen. bellman-ford gjør nettopp dette ganger.

BELLMAN-FORD(G, s):
  for hver node v: d[v] = uendelig, forelder[v] = ingen
  d[s] = 0
  gjenta |V| - 1 ganger:
      for hver kant (u, v) i G:
          RELAX(u, v)
  for hver kant (u, v) i G:          # ekstra runde: sjekk negativ sykel
      hvis d[u] + vekt(u, v) < d[v]:
          meld fra: negativ sykel finnes

Hvorfor er runder nok? En korteste vei kan ha høyst kanter, fordi en sti uten gjentatte noder ikke kan være lengre. Etter runde 1 er alle korteste veier med én kant funnet, etter runde 2 alle med to kanter, og slik forplanter riktig avstand seg én kant per runde. Etter runder er alt funnet. ƒbellman-ford

Den store styrken er at Bellman-Ford takler negative kantvekter.

452-63S:0A:4B:5C:-1D:2Negativ kant B→C: Bellman-Ford finner C:-1 via B
FIGNegativ kant — Bellman-Ford

Her er kanten B→C negativ med vekt . Selv om S→B koster mer enn S→A i utgangspunktet (5 mot 4), gir den negative kanten at korteste vei til C går via B: , bedre enn via A der . En grådig algoritme som låser avstanden til A tidlig ville bommet — men Bellman-Ford relakser alt på nytt og fanger rabatten.

Når korteste vei ikke finnes

Negative kanter åpner en farlig dør: hva om en sykel har negativ total vekt? Da finnes ingen negativ sykel-fri korteste vei, for vi kan gå rundt sykelen igjen og igjen og bli stadig billigere — avstanden går mot . Korteste vei er da udefinert.

Bellman-Ford oppdager dette gratis. Etter de rundene skal ingen kant kunne strammes inn mer hvis avstandene er ferdige. Kjører vi én ekstra runde og en kant fremdeles lar seg relakse, må det finnes en negativ sykel et sted, og algoritmen melder fra i stedet for å returnere et meningsløst svar. Det er den ekstra løkka nederst i pseudokoden over.

DAG-korteste-vei: lineær tid i en asyklisk graf

Hvis grafen er rettet og asyklisk — en DAG — kan vi gjøre det mye raskere enn Bellman-Ford, selv med negative kanter. Trikset er rekkefølgen: relaks kantene i dag-korteste-vei, altså i topologisk rekkefølge.

DAG-SHORTEST-PATHS(G, s):
  sorter nodene topologisk
  for hver node v: d[v] = uendelig, forelder[v] = ingen
  d[s] = 0
  for hver node u i topologisk rekkefølge:
      for hver nabo v av u:
          RELAX(u, v)

Når vi behandler en node i topologisk orden, er alle kanter inn til allerede relaksert, fordi de kom fra noder tidligere i ordenen. Da er ferdig, og vi kan trygt relaksere kantene ut. Hver kant relakses nøyaktig én gang, så det hele går i lineær tid. ƒdag-korteste-vei En DAG kan ikke ha sykler i det hele tatt, så negative sykler er et ikke-problem her.

Dijkstra: raskest, men krever ikke-negative vekter

Når alle vektene er ikke-negative — som faktiske kjøretider eller avstander — kan vi gjøre noe smartere enn å relakse alt på nytt. dijkstra plukker gjentatt den uavklarte noden med lavest estimat, erklærer den ferdig, og relakser bare kantene dens.

DIJKSTRA(G, s):
  for hver node v: d[v] = uendelig, forelder[v] = ingen
  d[s] = 0
  Q = alle noder i en prioritetskø på d
  S = {}                              # ferdige noder
  mens Q ikke er tom:
      u = EXTRACT-MIN(Q)
      S = S + {u}
      for hver nabo v av u:
          RELAX(u, v)                 # senker nøkkelen til v i Q

Den avgjørende observasjonen er at når en node trekkes ut som den nærmeste uavklarte, er estimatet dens allerede den endelige avstanden. Med ikke-negative vekter kan ingen senere oppdaget vei være kortere, for enhver omvei må gå via en node som er minst like langt unna. Derfor trenger Dijkstra aldri å revurdere en ferdig node — det er det som gjør den rask. Men nettopp denne antakelsen ryker med negative kanter: da kan en billig kant senere senke en avstand vi trodde var fastlåst, og Dijkstra gir feil svar. For negative kanter må du tilbake til Bellman-Ford.

Med en binærhaug koster de uttrekkene og de nøkkel-senkningene hver . ƒdijkstra med binærhaug Det gir , langt bedre enn Bellman-Fords . På eksempelgrafen i den første figuren tar Dijkstra nodene i rekkefølgen S (0), C (5), D (7), A (8), B (9), E (9) — hver gang den nærmeste som ennå ikke er ferdig.

Velg riktig verktøy

Tre algoritmer, ett problem, og valget styres av grafen. Er grafen asyklisk, gir DAG-korteste-vei svaret i lineær tid uansett fortegn på vektene. Har du negative kanter i en graf med sykler, må du bruke Bellman-Ford, som på kjøpet forteller deg om en negativ sykel gjør problemet uløselig. Er alle vektene ikke-negative, er Dijkstra det klart raskeste valget. Felles for alle tre er relax-operasjonen og trekantulikheten — det er bare rekkefølgen kantene strammes inn i som skiller dem.

Mine notater

Skriv egne notater for dette kapittelet. De lagres automatisk og dukker opp i «Mine notater»-oversikten paa fag-siden.

Laster…