Dijkstrin algoritam u Python & C++ (Primjer)
โก Pametni saลพetak
Dijkstrin algoritam izraฤunava najkraฤi put od jednog izvornog vrha do svakog drugog vrha u ponderiranom grafu s nenegativnim bridovima. Ova pohlepna metoda podupire Google Rutiranje mapa, usmjeravanje OSPF IP-a i bezbrojni sluฤajevi koriลกtenja najkraฤeg puta u mreลพi.

Koji je najkraฤi put ili najkraฤa udaljenost?
Put od izvornog vrha do odrediลกnog vrha koji koลกta minimalno je najkraฤi put ili najkraฤa udaljenost. U teoriji grafova moguฤe je imati viลกe ruta od izvora do odrediลกta. Meฤu tim rutama, ako postoji ruta koja koลกta minimalno, nazivamo je najkraฤim putem.
Ovdje โtroลกakโ znaฤi broj ฤvorova u ruti ili zbroj troลกkova na svakom bridu. Put moลพe imati jedan ili viลกe bridova. Veza izmeฤu dva vrha naziva se โbridโ. Postoje razliฤite vrste algoritama za najkraฤi put, kao ลกto su Dijkstrin algoritam i Bellman-Fordov algoritam.
Ovdje raspravljamo o Dijkstrinom algoritmu. Pogledajmo sljedeฤi ponderirani graf:
Neusmjereni ponderirani graf
- Pojam โponderiranoโ oznaฤava troลกak premjeลกtanja s jednog ฤvora na drugi. Na primjer, premjeลกtanjem s ฤvora 1 na ฤvor 2, troลกak ili ponder je 1.
- Put izmeฤu ฤvora 1 i ฤvora 2 naziva se rub.
- โNeusmjerenoโ znaฤi da se moลพete kretati s jednog ฤvora na drugi i natrag na prethodni ฤvor. Dakle, ako pokuลกamo pronaฤi sve rute od ฤvora 1 do ฤvora 7, one ฤe biti:
| Ruta ili put | Troลกak |
|---|---|
| 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 |
Meฤu ove ฤetiri rute, moลพemo vidjeti da prva ruta koลกta 7. Dakle, to je najkraฤi put u smislu troลกkova.
Najkraฤi put
Kako radi Dijkstrin algoritam
Dijkstrin algoritam moลพe pronaฤi najkraฤu udaljenost i u usmjerenim i u neusmjerenim ponderiranim grafovima. Ovaj algoritam je pohlepni jer uvijek bira najkraฤi ili najbliลพi ฤvor iz ishodiลกta. Pojam "pohlepni" znaฤi da ฤe algoritam izmeฤu skupa ishoda ili rezultata odabrati najbolji od njih.
Ovdje pokuลกavamo pronaฤi najkraฤe putove meฤu svim ostalim rutama. Dakle, Dijkstrin algoritam pronalazi sve najkraฤe putove od jednog izvornog ฤvora. Kao rezultat toga, ponaลกa se kao pohlepni algoritam.
U odjeljku "primjer" u nastavku vidjet ฤete postupni pristup. Funkcionira na sljedeฤi naฤin:
Korak 1) Inicijalizirajte poฤetni ฤvor s cijenom 0, a ostale ฤvorove s beskonaฤnom cijenom.
Korak 2) Odrลพavati niz ili listu za ฤuvanje track posjeฤenih ฤvorova.
Korak 3) Aลพurirajte troลกak ฤvora s minimalnim troลกkom. To se moลพe uฤiniti usporedbom trenutnog troลกka s troลกkom puta (prikazano u odjeljku s primjerima).
Korak 4) Nastavite korak 3 dok se ne posjete svi ฤvorovi.
Nakon dovrลกetka svih ovih koraka pronaฤi ฤemo put koji koลกta minimalno od izvora do odrediลกta.
Razlika izmeฤu Dijkstre i BFS, DFS
Glavna razlika izmeฤu Dijkstre i BFS-DFS-a je u tome ลกto je Dijkstra algoritam za pronalaลพenje najkraฤeg puta, dok su BFS i DFS opฤi algoritmi za pronalaลพenje puta. U opฤim sluฤajevima, BFS i DFS ne uzimaju u obzir cijenu ruba prilikom pronalaลพenja puta. Dakle, ovi algoritmi ne mogu jamฤiti najkraฤi put.
2D mreลพna demonstracija rada BFS-a
Algosketch, prikazuje BFS demonstraciju
Ova demonstracija pokazuje da BFS pronalazi samo put. Meฤutim, ne mari za teลพinu staze. BFS (Pretraลพivanje u ลกirinu) pretpostavlja da ฤe putovanje od jednog ฤvora do drugog koลกtati samo 1.
Pogledajmo primjer grafa:
Ovdje BFS pronalazi put na razini 2. BFS prelazi graf po razinama. Dakle, putuje ovako:
Korak 1) Poฤnite od ฤvora โ1โ i posjetite sve susjedne ฤvorove 2, 3, 4.
Korak 2) Oznaฤi ฤvorove 2, 3, 4 kao razinu 1 i posjeti njihove susjedne ฤvorove. Nastavit ฤe istraลพivati โโsve susjedne ฤvorove dok ne doฤe do odrediลกnog ฤvora.
ล to se tiฤe DFS-a, proฤi ฤe put od 1 do 7 na sljedeฤi naฤin:
- 1โ2โ3โ7 (Originalni troลกak 10, DFS troลกak 3)
- 1โ2โ6โ7 (Originalni troลกak 7, DFS troลกak 3)
- 1โ3โ7 (Originalni troลกak 8, DFS troลกak 2)
- 1โ4โ5โ7 (Originalni troลกak 13, DFS troลกak 3)
Kao ลกto vidimo, DFS izraฤunava cijenu puta s brojem bridova. DFS radi sljedeฤe:
- DFS moลพe pronaฤi put od izvora (poฤetnog vrha) do odrediลกta.
- Ne moลพe jamฤiti je li put otkriven od izvornog ฤvora do odrediลกta najkraฤi put ili nije.
Meฤutim, u smislu Dijkstrinog algoritma, on bira rubove na temelju njihove cijene. Kao pohlepni algoritam, odabrat ฤe putove s minimalnom cijenom.
Primjer Dijkstrinog algoritma
Dijkstrin algoritam koristi cijenu ili teลพinu za izraฤun ukupne cijene puta.
Cilj Dijkstrinog algoritma je minimizirati ovaj ukupni troลกak ili teลพinu. U gore prikazanom primjeru pronalazimo najbolje putove od ฤvora 1 do ฤvora 7, zatim izraฤunavamo sve troลกkove.
U Dijkstrinom algoritmu, pronaฤi ฤe se najkraฤi putevi izraฤunavanjem teลพina. Neฤe se traลพiti sve moguฤe puteve. Demonstrirajmo Dijkstrin algoritam na primjeru. Na primjer, zamoljeni ste da pronaฤete najkraฤi put od ฤvora 1 do 7.
U nastavku su navedeni koraci za ovaj postupak:
Korak 1) Inicijalizirajte poฤetnu cijenu ฤvora na 0. Dodijelite "Inf" do ostatka ฤvorova. To znaฤi da ne postoji put izmeฤu izvora i ฤvora ili put joลก nije posjeฤen.
Korak 2) Kada odaberete ฤvor 1, bit ฤe oznaฤen kao posjeฤen. Zatim aลพurirajte sve susjedne ฤvorove ฤvora 1. 2, 3, 4 su susjedni ฤvorovi ฤvora 1.
Dok aลพuriramo troลกak, moramo slijediti postupak u nastavku:
Troลกak svakog ฤvora moลพemo aลพurirati pomoฤu gornje formule. Na primjer, bili smo na ฤvoru 1 i trebali smo aลพurirati troลกak susjednih ฤvorova 2, 3 i 4. Nakon aลพuriranja, troลกkovi ฤe izgledati ovako:
Korak 3) Za ฤvor โ2โ, susjedi su 6 i 3. Aลพuriramo troลกak na โ6โ usporeฤujuฤi beskonaฤnost (trenutnu vrijednost) s troลกkom ฤvora 2 + troลกak puta od 2 do 6. Jednostavno reฤeno, ฤvor โ6โ imat ฤe troลกak 1 + 3 ili 4.
ฤvor 3 je susjed ฤvora 2. Meฤutim, izraฤunali smo njegov troลกak u prethodnom koraku, koji je bio 7. Sada, ako je naลก put 1-2-3, ฤvor 3 ฤe imati troลกak 10. Put 1-2- 3 ฤe koลกtati 10, dok ฤe 1 do 3 koลกtati 7.
Korak 4) Za ฤvor 3, susjedni ฤvor je 7. Dakle, usporeฤujuฤi trenutnu vrijednost ฤvora 7 s cijenom puta (7+1) ili 8, aลพurirat ฤemo cijenu ฤvora 7. To je 8. Dakle, pronalazimo put od ฤvora 1 do ฤvora 7, a on je 1โ3โ7. Cijena je 8.
Korak 5) Za ฤvor 4, aลพurirat ฤemo cijenu susjednog ฤvora u skladu s tim. Dakle, ฤvor "5" imat ฤe aลพuriranu cijenu od 8. Nakon koraka 4 i 5, izgledat ฤe ovako:
Sada put 1-3-7 ima cijenu 8 (prije). ฤvor "7" nije oznaฤen kao posjeฤen jer do ฤvora "7" moลพemo doฤi iz ฤvora "6". Put "1-2-6" imao je cijenu 4. Dakle, put 1-2-6-7 imat ฤe cijenu 7.
Kako je 7 < 8, najkraฤi put od izvornog vrha "1" do odrediลกnog vrha "7" bit ฤe 1-2-6-7, a cijena je 7. Prije je bila 1-3-7, a cijena je bila 8. Dakle, konaฤni graf ฤe izgledati ovako:
Rub oznaฤen crnom crtom je naลก najkraฤi put od 1 do 7, a koลกtat ฤe nas 7.
Nadimak Code Dijkstrin algoritam
Evo pseudokoda za Dijkstrin algoritam:
Dijkstra(G, S): for each vertex V in G distance[V] <- Infinity previous[V] <- NULL if V does not equal S, then, (priority queue) Q.push(V) distance[S] = 0 While Q is not empty U <- Extract the MIN from Q For each unvisited adjacent V of U TotalDistance <- distance[U] + edge_cost(U, V) if TotalDistance is less than distance[V], then distance[V] <- TotalDistance previous[V] <- U return distance, previous
C++ Implementacija Dijkstrinog algoritma
Za implementaciju Dijkstrinog algoritma pomoฤu C++, evo koda:
#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] && 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] && graph[U][j] && 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); }
Izlaz:
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 Implementacija Dijkstrinog algoritma
Za implementaciju Dijkstrinog algoritma pomoฤu Python, evo koda:
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] <= _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 < 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)
Izlaz:
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
Moลพemo vidjeti da algoritam izraฤunava najkraฤu udaljenost od izvornog ฤvora.
Primjena Dijkstra algoritma
Dijkstrin algoritam ima ลกirok raspon primjena. Meฤu njima, ลกiroko se koristi u podruฤju umreลพavanja. Evo nekih primjena Dijkstrinog algoritma u stvarnom ลพivotu:
Dijkstra u Google Karte: Ovaj algoritam je osnova za pronalaลพenje najkraฤih putova, kao ลกto moลพemo vidjeti iz gornjeg isjeฤka koda.
Google ne koristi jednostavni Dijkstrin algoritam. Umjesto toga, koristi modificiranu verziju. Kada odaberete odrediลกte, prikazuje vam viลกe putova u Google Karte. Meฤu tim putovima, neki su sortirani za korisnika. Ti se putovi odabiru na temelju โvremenaโ. Dakle, โvrijemeโ je cijena ruba za najkraฤi put.
Dijkstra u IP usmjeravanju: IP usmjeravanje je mreลพna terminologija. Opisuje kako se vaลก podatkovni paket ลกalje primatelju putem razliฤitih putova. Ti putovi se sastoje od usmjerivaฤa, posluลพitelja i druge opreme. U IP usmjeravanju postoje razliฤite vrste protokola.
Ovi protokoli pomaลพu usmjerivaฤu da pronaฤe najkraฤe putove za slanje podataka. Jedan od naziva protokola je โOSPF (Open Shortest Path First - Prvo Otvori Najkraฤi Put)โ. OSPF koristi Dijkstrin algoritam. Usmjerivaฤ odrลพava tablicu ruta. Svaki usmjerivaฤ dijeli svoju tablicu sa susjednim usmjerivaฤima. Nakon ลกto prime aลพuriranu tablicu, moraju ponovno izraฤunati sve putove. U tom trenutku usmjerivaฤ koristi Dijkstrin algoritam.
Ograniฤenje Dijkstrinog algoritma
Dijkstrin algoritam ne moลพe jamฤiti najkraฤi put u grafu s negativnim bridovima. Dijkstrin algoritam slijedi ove principe:
- Od jednog ฤvora do drugog iฤi ฤe jedan najkraฤi put.
- Jednom kada se odabere najkraฤi put izmeฤu dva ฤvora, neฤe se ponovno izraฤunati.
Ovdje primijetite dva primjera s negativnim rubovima.
Na lijevom grafu, Postoje tri vrha. Dijkstra ฤe se izvrลกavati na grafu na sljedeฤi naฤin:
Korak 1) Poฤetni vrh "1" bit ฤe inicijaliziran na nulu. Ostali ฤvorovi ฤe imati beskonaฤnost.
Korak 2) Oznaฤi ฤvor "1" kao posjeฤen i uvrsti ga u najkraฤi put.
Korak 3) Udaljenost izvornog ฤvora 1 do ฤvorova โ2โ i โ3โ postavljena je na beskonaฤnost, buduฤi da najkraฤi put tek treba izraฤunati. Dakle, svaki put koji koลกta manje od beskonaฤnosti bit ฤe dodan najkraฤem putu (pohlepni pristup).
Korak 4) Aลพuriranje udaljenosti od izvornog vrha "1" na "2". Trenutna teลพina bit ฤe 5 (5 < beskonaฤno). Sliฤno tome, aลพurirajte udaljenost od ฤvora "1" na "3" s teลพinom 3.
Korak 5) Ako sada provjerimo najkraฤe udaljenosti od ฤvora โ1โ, otkrit ฤemo da je 5 najkraฤa udaljenost za rub 1โ2. Dakle, ฤvor โ2โ bit ฤe oznaฤen kao posjeฤen. Sliฤno tome, ฤvor โ3โ takoฤer ฤe biti oznaฤen kao posjeฤen jer je najkraฤa udaljenost 3.
Meฤutim, ako promatramo, postoji put 1-3-2 koji ฤe koลกtati samo 2. Ali Dijkstra pokazuje da je od ฤvora "1" do ฤvora "2" najkraฤa udaljenost 5. Dakle, Dijkstra nije ispravno izraฤunao najkraฤu udaljenost. Razlog je taj ลกto je Dijkstra pohlepni algoritam. Dakle, nakon ลกto je ฤvor oznaฤen kao posjeฤen, neฤe se ponovno razmatrati, iako bi mogao biti dostupan kraฤi put. Ovaj problem se javlja samo kada rubovi imaju negativne troลกkove ili rubove negativne teลพine.
Dijkstra ne uspijeva izraฤunati najkraฤi put izmeฤu dva ฤvora u ovom scenariju. Kao rezultat toga, ovaj algoritam ima neke nedostatke. Za rjeลกavanje ovog problema negativnih rubova koristi se drugi algoritam nazvan "Bellman-Fordov algoritam". Taj algoritam moลพe raditi s negativnim rubovima.
Sloลพenost Dijkstrinog algoritma
Gornja implementacija koristila je dvije petlje "za". Ove se petlje pokreฤu za odreฤeni broj vrhova. Dakle, vremenska sloลพenost je O(Vยฒ)Ovdje je izraz โOโ oznaka koja daje pretpostavku za Dijkstrin algoritam.
Graf moลพemo pohraniti pomoฤu "reda prioriteta". Red prioriteta je binarna struktura podataka hrpe. Bit ฤe uฤinkovitija od 2D matrice. Rub s minimalnim troลกkovima imat ฤe visoki prioritet. Tada ฤe vremenska sloลพenost biti O(E log V). Ovdje je E broj bridova, a V broj vrhova.
Sloลพenost prostora je O(Vยฒ), buduฤi da koristimo matricu susjedstva (2D niz). Sloลพenost prostora moลพe se optimizirati koriลกtenjem popisa susjedstva ili strukture podataka reda ฤekanja.















