Dijsktra algoritmusa: C++, Python Kódpélda

Mi a legrövidebb út vagy legrövidebb távolság?

A forráscsúcstól a célcsúcsig vezető út, amely minimális költséggel jár, a legrövidebb út vagy a legrövidebb távolság. A gráfelméletben több útvonal is lehetséges a forrástól a célállomásig. Ezen útvonalak között, ha van olyan útvonal, amely minimális költséggel jár, azt nevezhetjük a legrövidebb út algoritmusának.

Itt a „költség” az útvonal csomópontjainak számát vagy az egyes útvonalakon lévő költségek összegét jelenti. Egy útvonalnak egy vagy több éle lehet. A két csúcs közötti kapcsolatot élnek nevezzük. Különféle típusú legrövidebb útvonalú algoritmusok léteznek, például Dijkstra algoritmusa, Bellman-Ford algoritmus stb.

Itt a Dijkstra algoritmusáról beszélünk

Nézzük a következő súlyozott grafikont:

Irányítatlan súlyozott grafikon
Irányítatlan súlyozott grafikon
  • A „súlyozott” kifejezés a költségek egyik csomópontról a másikra való áthelyezését jelenti. Például az 1. csomópontról a 2. csomópontra lépve a költség vagy a súly 1.
  • Az 1-es csomópont és a 2-es csomópont közötti utat élnek nevezzük.
  • Az „iránytalan” azt jelenti, hogy áthelyezhet egy csomópontot a másikba, majd vissza az előző csomópontra. Tehát, ha megpróbáljuk megtalálni az összes útvonalat az 1-es csomóponttól a 7-es csomópontig, akkor ez lesz:
Útvonal vagy útvonal Költség
1-2-6 7- (1+3+3) = 7
1-2-3 7- (1+9+1) = 11
1-3-7 (7+1) = 8
1-4-5 7- (6+2+5) = 13

Ebből a négy útvonalból láthatjuk, hogy az első út 7-be fog kerülni. Tehát költség szempontjából ez a legrövidebb út.

A legrövidebb út

A legrövidebb út

Hogyan működik Dijkstra algoritmusa

A Dijkstra algoritmus képes megtalálni a legrövidebb távolságot irányított és irányítatlan súlyozott gráfokban egyaránt. Ez az algoritmus mohó, mert mindig a legrövidebb vagy legközelebbi csomópontot választja az origóból. A „mohó” kifejezés azt jelenti, hogy az eredmények vagy eredmények halmaza közül az algoritmus a legjobbat választja ki közülük.

Itt igyekszünk megtalálni a legrövidebb utakat az összes többi útvonal közül. Tehát a Dijkstra algoritmusa megtalálja az összes legrövidebb utat egyetlen célcsomóponttól. Ennek eredményeként úgy viselkedik, mint a mohó algoritmus.

Az alábbi „példa” részben lépésről lépésre láthatja a megközelítést. A következőképpen működik:

Step 1) Inicializálja a kezdő csomópontot 0 költséggel, a csomópont többi részét pedig végtelen költséggel.
Step 2) Tartson fenn egy tömböt vagy listát a meglátogatott csomópontok nyomon követéséhez
Step 3) Frissítse a csomópont költségét a minimális költséggel. Megtehető az aktuális költség és az útköltség összehasonlításával. (A bemutató a példa részben látható)
Step 4) Folytassa a 3. lépést, amíg az összes csomópontot meg nem látogatja.

Mindezen lépések elvégzése után megtaláljuk azt az utat, amely minimális költséggel jár a forrástól a célig.

Különbség a Dijkstra és a BFS, DFS között

A fő különbség a Dijkstra és a BFS-DFS között az, hogy a Dijkstra a legrövidebb útkereső algoritmus, a BFS, DFS pedig az útkereső algoritmus. Általában a BFS és a DFS nem veszi figyelembe a költségeket az útvonal megtalálásakor. Tehát ezek az algoritmusok nem tudják garantálni a legrövidebb utat.

2D rács bemutató a BFS működéséről

2D rács bemutató

Algosketch, BFS bemutatót mutat

Ez a demonstráció azt jelzi, hogy a BFS csak az utat találja meg. Azonban nem törődik az ösvény súlyával. BFS (Breadth-First Search) feltételezi, hogy az egyik csomópontból a másikba való utazás csak 1-be kerül.

De lássunk egy példagrafikont:

2D rács bemutató

Itt talál egy utat a 2. szinten. A BFS a grafikonon szintrendben halad. Tehát így utazik:

Step 1) Kezdje az „1” csomóponttól, és keresse fel az összes szomszédos 2,3,4, XNUMX, XNUMX csomópontot

Step 2) Jelölje meg a 2,3,4., 1., XNUMX. csomópontot XNUMX. szintként, és keresse fel a szomszédos csomópontjaikat. Folytatja az összes szomszédos csomópont feltárását, amíg el nem éri a célcsomópontot.

Az elosztott fájlrendszer szempontjából az 1-től 7-ig terjedő útvonalat a következőképpen haladja meg:

  • 1→2→3→7 (eredeti költség 10, DFS költség 3)
  • 1→2→6→7 (eredeti költség 7, DFS költség 3)
  • 1→3→7 (Eredeti költség 8, DFS költség 2)
  • 1→4→5→7 (eredeti költség 13, DFS költség 3)

Amint látjuk, a DFS a mélység vagy az élek számával számítja ki az útköltséget.

A DFS a következőket teszi:

  • Az elosztott fájlrendszer megtalálja a forrástól (a kezdőcsúcstól) a célig vezető utat.
  • Nem tudja garantálni, hogy a forráscsomóponttól a célig felfedezett út a legrövidebb út-e vagy sem.

A Dijkstra algoritmus szempontjából azonban a költségük alapján választja ki az éleket. Mohó algoritmusként a legjobb vagy a minimális költségű utat választja ki.

Példa Dijkstra algoritmusára

A Dijkstra algoritmusa a költséget vagy a súlyt használja az útvonal teljes költségének kiszámításához.

Példa Dijkstra algoritmusára

A Dijkstra algoritmusának célja ennek a teljes költségnek vagy súlynak a minimalizálása. A fenti példában megtaláljuk a legjobb útvonalakat az 1. csomóponttól a 7. csomópontig, majd kiszámítjuk az összes költséget.

A Dijkstra algoritmusában súlyszámítással találja meg a legrövidebb utakat. Nem fog minden lehetséges utat megkeresni. Mutassuk meg Dijkstra algoritmusát egy példán keresztül. Például arra kérték, hogy keresse meg a legrövidebb utat az 1-es csomóponttól a 7-ig.

Ennek a folyamatnak a lépései az alábbiak:

Step 1) Inicializálja a kezdő csomópont költségét 0-ra.

A csomópont többi része, hozzárendelés „Inf”. Ez azt jelenti, hogy nincs út a kezdő csúcs (a forrás) és a csomópont között, vagy az útvonal még nincs meglátogatva.

Példa Dijkstra algoritmusára

Step 2) Amikor kiválasztja az 1. csomópontot, a rendszer látogatottként jelöli meg. Ezután frissítse az 1. csomópont összes szomszédos szomszédját. 2,3,4 az 1. csomópont szomszédos csomópontjai.

A költség frissítése során az alábbi eljárást kell követnünk:

Példa Dijkstra algoritmusára

A fenti képlet segítségével frissíthetjük az egyes csomópontok költségét. Például az 1. csomópontnál voltunk, és frissítenünk kellett a szomszédos 2,3,4, XNUMX, XNUMX csomópont költségeit.

A frissítés után a költség vagy a súly így fog kinézni:

Példa Dijkstra algoritmusára

3. lépés) A „2” csomóponthoz a szomszédok a 6 és a 3. A költséget „6”-ra frissítjük a végtelen (aktuális érték) összehasonlításával. A 2. csomópont költsége + az útvonal költsége 2-től 6-ig. Egyszerűen mondva, a „6” csomópont költsége 1+3 vagy 4 lesz.

Példa Dijkstra algoritmusára

A 3. csomópont a 2. csomópont szomszédja. Az előző lépésben azonban kiszámoltuk a költségét, ami 7 volt. Most, ha az útvonalunk 1-2-3, a 3. csomópont költsége 10 lesz. 1-2- 3 ára 10, míg 1-3 7.

4. lépés) A 3. csomóponthoz a szomszédos csomópont 7. Tehát a 7. csomópont aktuális értékét összehasonlítva az útköltséggel (7+1) vagy 8-mal, frissítjük a 7. csomópont költségét. Ez a 8.

Tehát találunk egy utat az 1-es csomóponttól a 7-es csomópontig, és ez 1→3→7. A költség 8.

5. lépés) A 4. csomóponthoz ennek megfelelően frissítjük a szomszédos csomópont költségét. Tehát az „5” csomópont frissített költsége 8 lesz. A 4,5 lépés után ez így fog kinézni:

Példa Dijkstra algoritmusára

Most az 1-3-7 útvonal ára 8 volt (korábban). A „7” csomópont nem lett meglátogatott, mert a „7” csomópontot a „6” csomópontból tudjuk elérni. Az „1-2-6” útvonal költsége 4 volt. Tehát az 1-2-6-7 útvonal költsége 7 lesz.

Mivel 7 < 8, ezért az „1” forráscsúcstól a „7” célcsúcsig a legrövidebb út 1-2-6-7 lesz, a költség pedig 7. Korábban 1-3-7 volt, és a költség 8 volt.

Tehát a végső grafikon így fog kinézni:

Példa Dijkstra algoritmusára

A fekete vonallal jelölt él a legrövidebb utunk 1-től 7-ig, és 7-be fog kerülni.

Pseudo Code Dijkstra algoritmusa

Íme a Dijkstra algoritmusának pszeudokódja

Dijkstra(G, S):  
  for each vertex V in G
    distance[V] & lt; - Infinity
    previous[V] & lt; - NULL
  if V does not equal S, then, 
    (priority queue) Q.push(V)
  distance[S] = 0
  While Q is not empty
   U & lt; - Extract the MIN from Q
   For each unvisited adjacent V of U
   TotalDistance & lt; - distance[U] + edge_cost(U, V)
   if TotalDistance is less than distance[V], then
     distance[V] & lt; - TotalDistance
     previous[V] & lt; - u
  return distance, previous

C++ Dijkstra algoritmusának megvalósítása

Dijkstra algoritmusának megvalósításához a C++, itt a kód:

#include <bits/stdc++.h>
using namespace std;
#define size 7
int minimumDistance(int distance[], bool visited[]) {
  int min = INT_MAX;
  int min_index = INT_MAX;
  for (int i = 0; i < size; i++) {
    if (!visited[i] & amp; & distance[i] <= min) {
      min = distance[i];
      min_index = i;
    }
  }
  return min_index;
}
void printParentPath(int parent[], int i) {
  if (parent[i] == -1) {
    return;
  }
  printParentPath(parent, parent[i]);
  cout << i + 1 << " ";
}
void dijkstra(int graph[size][size], int source) {
  int distance[size];
  bool visited[size];
  int parent[size];
  for (int i = 0; i < size; i++) {
    parent[0] = -1;
    distance[i] = INT_MAX;
    visited[i] = false;
  }
  distance[source] = 0;
  for (int i = 0; i < size - 1; i++) {
    int U = minimumDistance(distance, visited);
    visited[U] = true;
    for (int j = 0; j < size; j++) {
      int curr_distance = distance[U] + graph[U][j];
      if (!visited[j] & amp; & graph[U][j] & amp; &
          curr_distance < distance[j]) {
        parent[j] = U;
        distance[j] = curr_distance;
      }
    }
  }
  cout << "Vertex\t\tDistance\tPath" << endl;
  for (int i = 1; i < size; i++) {
    cout << source + 1 << "->" << i + 1 << "\t\t" << distance[i] << "\t\t"
         << source + 1 << " ";
    printParentPath(parent, i);
    cout << endl;
  }
}
int main() {
  int graph[size][size] = {{0, 1, 7, 6, 0, 0, 0}, {1, 0, 9, 0, 0, 3, 0},
                           {7, 9, 0, 0, 0, 0, 1}, {6, 0, 0, 0, 2, 0, 0},
                           {0, 0, 0, 2, 0, 0, 0}, {0, 3, 0, 0, 0, 0, 3},
                           {0, 0, 0, 0, 5, 3, 0}};
  dijkstra(graph, 0);
}

output:

Vertex     Distance        Path

1->2           1             1 2
1->3           7             1 3
1->4           6             1 4
1->5           8             1 4 5
1->6           4             1 2 6
1->7           7             1 2 6 7

Python Dijkstra algoritmusának megvalósítása

Dijkstra algoritmusának megvalósításához a piton, itt a kód:

num_of_vertex = 7
def minimumDistance(distance, visited): 
  _min = 1e11
  min_index = 1e11
for i in range(num_of_vertex): 
  if not visited[i] and distance[i] & lt; = _min: 
   _min = distance[i]
   min_index = i
return min_index
def printParentNode(parent, i): 
  if parent[i] == -1: 
   return
  printParentNode(parent, parent[i])
  print("{} ".format(i + 1), end = "")
def dijkstra(graph, src): 
  distance = list()
  visited = list()
  parent = list()
for i in range(num_of_vertex): 
  parent.append(-1)
  distance.append(1e11)
  visited.append(False)
distance[src] = 0
for i in range(num_of_vertex - 1): 
  U = minimumDistance(distance, visited)
  visited[U] = True
for j in range(num_of_vertex): 
  curr_distance = distance[U] + graph[U][j]
  if not visited[j] and graph[U][j] and curr_distance & lt;
    distance[j]: parent[j] = U
    distance[j] = curr_distance
  print("Vertex\t\tDistance\tPath")
for i in range(num_of_vertex): 
  print("{}->{}\t\t{}\t\t{} 
".format(src + 1, i + 1, distance[i], src + 1), end = "")
  printParentNode(parent, i)
print("")
graph = [
  [0, 1, 7, 6, 0, 0, 0],
  [1, 0, 9, 0, 0, 3, 0],
  [7, 9, 0, 0, 0, 0, 1],
  [6, 0, 0, 0, 2, 0, 0],
  [0, 0, 0, 2, 0, 0, 0],
  [0, 3, 0, 0, 0, 0, 3],
  [0, 0, 0, 0, 5, 3, 0]
]
dijkstra(graph, 0)

output:

Vertex     Distance        Path

1->1           0              1
1->2           1              1 2
1->3           7              1 3
1->4           6              1 4
1->5           8              1 4 5
1->6           4              1 2 6
1->7           7              1 2 6 7

Láthatjuk, hogy az Algoritmus a forráscsomóponttól számított legrövidebb távolságot számítja ki.

Dijkstra algoritmus alkalmazása

A Dijkstra Algo számos felhasználási területtel rendelkezik. Ezek közül széles körben használják a hálózatépítés területén. Íme néhány a Dijkstra algoritmus valós használatából:

Dijkstra a Google térképen: Alapvetően ez az algoritmus a gerince a legrövidebb utak megtalálásának. Ahogy a fenti kódrészlet kimenetéből láthatjuk.

Dijkstra algoritmus alkalmazása

A Google nem az egyszerű Dijkstra algoritmust használja. Ehelyett egy módosított verziót használ. Amikor kiválaszt egy úti célt, több útvonalat is megjelenít a Google Térképen. Ezen elérési utak közül néhányat a rendszer a felhasználó számára rendez. Ezek a kiválasztott utak az „idő” alapján történtek. Tehát az „idő” a legrövidebb út szélső költsége.

Dijkstra az IP-útválasztásban: IP-útválasztás egy hálózati terminológia. Ez azt jelenti, hogy az Ön adatcsomagja hogyan kerül elküldésre a vevőnek különböző utakon. Ezek az útvonalak útválasztókat, szervereket stb. tartalmaznak. Az IP-útválasztásban különböző típusú protokollok léteznek.

Ezek a protokollok segítenek az útválasztónak megtalálni a legrövidebb útvonalat az adatok küldéséhez. Az egyik protokoll neve „OSPF (Open Shortest Path First)”. Az OSPF dijkstra algoritmust használ. A router vezet egy útvonaltáblázatot. Minden útválasztó megosztja a táblázatát a szomszédos útválasztókkal. A frissített táblázat kézhezvétele után újra ki kell számítaniuk az összes elérési utat. Ekkor az útválasztó a Dijkstra algoritmust használja.

Dijkstra algoritmusának korlátozása

A Dijkstra algoritmus nem tudja garantálni a legrövidebb utat a negatív élgráfban. A Dijkstra algoritmus a következő módszereket követi:

  • Az egyik legrövidebb út az egyik csomóponttól a másikig vezet.
  • A két csomópont közötti legrövidebb út kiválasztása után a rendszer nem számítja ki újra.

Itt figyeljen meg két negatív élű példát.

Dijkstra algoritmusának korlátozása

A bal oldali grafikonon három csúcs van. A Dijkstra a következőképpen fog futni a grafikonon:

Step 1) Az „1” kezdőcsúcs nullára lesz inicializálva. A többi csomópont végtelen lesz.

Dijkstra algoritmusának korlátozása

Step 2) Jelölje meg az „1” csomópontot látogatottként, és vegye fel a legrövidebb útvonalba.

Step 3) Az 1-es forráscsomópont távolsága a „2” és „3” csomópontoktól a végtelenbe van állítva, mivel a legrövidebb utat még ki kell számítani. Tehát minden olyan út, amely a végtelennél kevesebbe kerül, hozzáadódik a legrövidebb úthoz (mohó megközelítés).

Step 4) Az „1” forráscsúcs vagy a forráscsomópont távolságának frissítése „2”-re. A jelenlegi súly 5 (5

Dijkstra algoritmusának korlátozása

Step 5) Ha most megvizsgáljuk a legrövidebb távolságokat az „1” csomóponttól, azt találjuk, hogy az 5 az 1–> 2 él legrövidebb távolsága. Tehát a „2” csomópont látogatottként lesz megjelölve.

Hasonlóképpen, a „3” csomópont is látogatottként lesz megjelölve, mivel a legrövidebb távolság a 3.

Azonban, ha megfigyeljük, van egy 1-3-2 út, ami csak 2-be kerül. De a Dijkstra azt mutatja, hogy az „1” csomóponttól a „2” csomópontig a legrövidebb távolság 5. Tehát Dijkstra nem tudta kiszámítani a legrövidebbet távolságot helyesen. Ennek az az oka, hogy a Dijkstra egy mohó algoritmus. Tehát, ha egy csomópont látogatottnak van jelölve, a rendszer nem vizsgálja felül, bár lehet, hogy rövidebb elérési út áll rendelkezésre. Ez a probléma csak akkor fordul elő, ha az élek negatív költséggel vagy negatív súlyú élekkel rendelkeznek

A Dijkstra ebben a forgatókönyvben nem tudja kiszámítani a két csomópont közötti legrövidebb utat. Ennek eredményeképpen ennek az algoritmusnak vannak hátrányai. A negatív élprobléma megoldására egy másik „Bellman-Ford Algorithm” nevű algoritmust használnak. Ez az algoritmus negatív élekkel is működhet.

Dijkstra algoritmusának összetettsége

A fenti megvalósítás két „for” hurkot használt. Ezek a hurkok a csúcsok számára futnak. Tehát az idő bonyolultsága O(V2). Itt a „0” kifejezés olyan jelölést jelent, amely a Dijkstra algoritmusra vonatkozó feltételezést ad.

A grafikont a „Priority Queue” segítségével tárolhatjuk. A prioritási sor egy bináris kupac adatstruktúra. Hatékonyabb lesz, mint egy 2D mátrix. A minimális költséggel rendelkező él kiemelt prioritást élvez.

Akkor az idő bonyolultsága lesz O(E log(V)). Itt E az élek száma, V pedig a csúcsok száma.

A tér összetettsége az O(V2), mivel szomszédsági mátrixot használunk (2D tömb). A tér összetettsége optimalizálható szomszédsági lista vagy sor adatstruktúra segítségével.