Kortstepad-algoritme

Het kortstepad-algoritme, ook bekend als Dijkstra's algoritme, is een graaf-algoritme beschreven door Edsger Dijkstra in 1959.[1] Gegeven een gewogen graaf G {\displaystyle G} waarin de afstand tussen ieder tweetal verbonden punten van G {\displaystyle G} ten minste 0 bedraagt, rekent het algoritme voor een bepaalde beginknoop g b G {\displaystyle g_{b}\in G} de kortste afstand uit tot alle punten van G {\displaystyle G} . Toepassingen van dit algoritme zijn onder meer bij verkeersmodellen, route-navigatiesystemen en telecommunicatie.

De basis

Het algoritme is gebaseerd op de opmerking dat de afstand (de lengte van het kortste pad) tussen ieder tweetal punten v {\displaystyle v} en w {\displaystyle w} van een gewogen graaf G {\displaystyle G} als volgt berekend kan worden:

Zij d ( x ) {\displaystyle d(x)} de afstand tussen v {\displaystyle v} en x {\displaystyle x} , voor x {\displaystyle x} een punt van G {\displaystyle G} .
Zij N w = { y G y w } {\displaystyle N_{w}=\{y\in G\mid y\rightarrow w\}} , de verzameling van punten y {\displaystyle y} die direct verbonden zijn met w (via een tak van y {\displaystyle y} naar w {\displaystyle w} ).
Zij g e w ( y , w ) {\displaystyle gew(y,w)} het gewicht van de tak tussen twee direct met elkaar verbonden knopen y {\displaystyle y} en w {\displaystyle w} .
De afstand van v {\displaystyle v} naar w {\displaystyle w} is d ( w ) = min { d ( y ) + g e w ( y , w ) y N w } {\displaystyle d(w)=\min\{d(y)+gew(y,w)\mid y\in N_{w}\}} , het minimum van de som van de afstand van v {\displaystyle v} naar een punt y N {\displaystyle y\in N} plus de directe afstand van dat punt naar w {\displaystyle w} . Oftewel - weinig verrassend - als de som van alle directe afstanden in een pad minimaal is, dan is de totale lengte van dat pad (die som dus) minimaal.
Verder definiëren we M v = { y G v y } {\displaystyle M_{v}=\{y\in G\mid v\rightarrow y\}} , de verzameling van punten y {\displaystyle y} die direct verbonden zijn met v {\displaystyle v} (via een tak van v {\displaystyle v} naar y {\displaystyle y} ).

Het algoritme

Het algoritme is eigenlijk een veralgemening van de basis van hierboven. Het algoritme verdeelt de punten van G {\displaystyle G} in drie verzamelingen:

A {\displaystyle A} : de verzameling van punten waarvan de kortste afstand tot g b {\displaystyle g_{b}} berekend is
X {\displaystyle X} : de verzameling van punten waarnaar er al wel een pad bekend is vanuit g b {\displaystyle g_{b}} , maar niet het kortste, of dit is nog niet vastgesteld.
V = G A X {\displaystyle V=G-A-X} : de overige punten van {\displaystyle } (deze verzameling wordt niet bijgehouden in het algoritme)

Hiervoor geldt uiteraard dat A X = {\displaystyle A\cap X=\emptyset } , A {\displaystyle A} en X {\displaystyle X} hebben geen punten gemeen.

Daarnaast bestaat er een array d ( v ) {\displaystyle d(v)} , geïndiceerd met de punten v {\displaystyle v} van G {\displaystyle G} . Voor elk punt g G {\displaystyle g\in G} wordt dit array door het algoritme dusdanig gevuld dat aan het eind geldt d ( g ) = {\displaystyle d(g)=} de lengte van het kortste pad van g b {\displaystyle g_{b}} naar g {\displaystyle g} .

Initieel geldt

  • A = { g b } {\displaystyle A=\{g_{b}\}}
  • X = M g b {\displaystyle X=M_{g_{b}}}
  • d ( g b ) = 0 {\displaystyle d(g_{b})=0}
  • g X : d ( g ) = g e w ( g b , g ) {\displaystyle \forall {g\in X}:d(g)=gew(g_{b},g)}
  • g V ( G ) X A : d ( g ) = {\displaystyle \forall {g\in V(G)-X-A}:d(g)=\infty }

Het algoritme herhaalt nu de volgende stappen totdat X {\displaystyle X} de lege verzameling wordt (op dat moment zijn er geen bereikbare punten meer over), vanuit g b {\displaystyle g_{b}} :

  1. Kies uit X {\displaystyle X} het punt x {\displaystyle x} met de minimale waarde van d ( x ) {\displaystyle d(x)} ; dit is de eindwaarde van d ( x ) {\displaystyle d(x)} voor dat punt. Immers, d ( x ) {\displaystyle d(x)} heeft de waarde van de lengte van het kortste pad naar x {\displaystyle x} vanuit g b {\displaystyle g_{b}} dat we tot nu toe gezien hebben. Ieder ander pad naar x {\displaystyle x} moet over x {\displaystyle x} lopen en is dus langer, omdat alle kanten een positieve lengte hebben.
  2. Omdat d ( x ) {\displaystyle d(x)} definitief is, verplaatsen we x {\displaystyle x} van X {\displaystyle X} naar A {\displaystyle A} . Voor alle punten z {\displaystyle z} die bereikbaar zijn vanuit x {\displaystyle x} en die nog niet in A {\displaystyle A} zitten, doen we het volgende:
    1. Zit z {\displaystyle z} nog niet in X {\displaystyle X} , dan voegen we het punt aan X {\displaystyle X} toe. Onze eerste schatting voor de afstand tussen g b {\displaystyle g_{b}} en z {\displaystyle z} is dan d ( x ) + g e w ( x , z ) {\displaystyle d(x)+gew(x,z)} -- deze waarde plaatsen we in d ( z ) {\displaystyle d(z)}
    2. Zit z {\displaystyle z} al wel in X {\displaystyle X} , dan passen we de schatting in d ( z ) {\displaystyle d(z)} aan -- de nieuwe waarde is het minimum van de lengte van het nieuw-gevonden pad naar z {\displaystyle z} ( d ( x ) + g e w ( x , z ) {\displaystyle d(x)+gew(x,z)} ) en de lengte van het kortste pad naar z {\displaystyle z} dat we al gevonden hadden.

Zodra dit algoritme afloopt (en dat doet het, want V ( G ) {\displaystyle V(G)} is eindig en iedere stap verplaatst een element van X {\displaystyle X} naar A {\displaystyle A} ), dan is d ( v ) {\displaystyle d(v)} gevuld met de afstanden van g b {\displaystyle g_{b}} naar alle punten die vanuit dit beginpunt bereikbaar zijn. Is d ( w ) {\displaystyle d(w)} oneindig voor een w V ( G ) {\displaystyle w\in V(G)} , dan is dat punt w {\displaystyle w} niet bereikbaar vanuit g b {\displaystyle g_{b}} .

Algoritme in pseudocode

In pseudocode ziet het algoritme er als volgt uit:

 foreach 
  
    
      
        v
        
        V
        (
        G
        )
      
    
    {\displaystyle v\in V(G)}
  
 do 
  
    
      
        d
        (
        v
        )
        =
        
      
    
    {\displaystyle d(v)=\infty }
  
;
 A := 
  
    
      
        
          g
          
            b
          
        
      
    
    {\displaystyle g_{b}}
  

 d(a) := 0
 X := 
  
    
      
        
      
    
    {\displaystyle \emptyset }
  

 foreach z : z 
  
    
      
        
      
    
    {\displaystyle \in }
  
 
  
    
      
        
          M
          
            (
            
              g
              
                b
              
            
            )
          
        
      
    
    {\displaystyle M_{(g_{b})}}
  
 do 
   X := X 
  
    
      
        
      
    
    {\displaystyle \cup }
  
 {z}
   d(z) := gew(
  
    
      
        
          g
          
            b
          
        
      
    
    {\displaystyle g_{b}}
  
,z); 
   /* X en d zijn nu geïnitialiseerd */
 while not(X = 
  
    
      
        
      
    
    {\displaystyle \emptyset }
  
) do
   /* X is nog niet leeg */
   y : (y 
  
    
      
        
      
    
    {\displaystyle \in }
  
 X) 
  
    
      
        
      
    
    {\displaystyle \land }
  
 (d(y) = MIN {d(y')|y' 
  
    
      
        
      
    
    {\displaystyle \in }
  
 X}
   /* y is dus het element van X met de laagste waarde van d(v) -- dit is de definitieve waarde van d(y) */
   A := A 
  
    
      
        
      
    
    {\displaystyle \cup }
  
 {y}
   X := X
  
    
      
        
      
    
    {\displaystyle \setminus }
  
{y} 
   /* y is nu verplaatst van X naar A */
   foreach z: z 
  
    
      
        
      
    
    {\displaystyle \in }
  
 
  
    
      
        
          M
          
            (
            y
            )
          
        
      
    
    {\displaystyle M_{(y)}}
  
 
  
    
      
        
      
    
    {\displaystyle \land }
  
 not (z 
  
    
      
        
      
    
    {\displaystyle \in }
  
 A) do 
     if not (z 
  
    
      
        
      
    
    {\displaystyle \in }
  
 X) then
        X := X 
  
    
      
        
      
    
    {\displaystyle \cup }
  
 {z} 
        d(z) := d(y) + gew(y,z)
     else 
    /* dus z 
  
    
      
        
      
    
    {\displaystyle \in }
  
 X */ 
        d(z) := MIN{d(z), d(y) + gew(y,z)}

Hedendaagse toepassingen

Het algoritme wordt nog altijd veel toegepast in navigatieapparatuur, zoals de TomTom voor autoroutes of NavKid voor bootroutes. Daarbij wordt een wijziging toegepast; de afstand hoeft niet de echte afstand te zijn, maar kan ook worden uitgedrukt in tijd (voor de snelste route), in energieverbruik (de zuinigste route) of in straf/bonuspunten (voor bijvoorbeeld een mooie route, route met weinig afslagen, of een veilige route). Het principe van "afstand" blijft dan, maar er worden andere grootheden gebruikt.

Om het algoritme in de praktijk te kunnen gebruiken, zijn er enkele aanpassingen nodig, omdat niet de hele graaf (kaart) in het geheugen past. Ook kunnen er beperkingen zijn zoals de afmetingen van het voertuig of vaartuig, en bepaalde verkeersinformatie of stremmingen.

Bronnen, noten en/of referenties
  1. Dijkstra, E., A note on two problems in connexion with graphs. In: Numerische Mathematik. 1 (1959), blz. 269–271