Implementacja algorytmu Dijkstry
Implementacja algorytmu Dijkstry w języku Python
W tej sekcji zapoznamy się z implementacją algorytmu Dijkstry w języku Python.
Specyfikacja problemu:
Dane:
listaSasiedztwa– lista sąsiedztwa spójnego grafu nieskierowanego o dodatnich wagachs– indeks wierzchołka początkowego grafuV– liczba naturalna dodatnia; liczba wierzchołków grafu
Wynik:
tablica przechowująca najkrótsze ścieżki od wierzchołka
sdo pozostałych wierzchołków oraz tablica przechowująca poprzedniki kolejnych wierzchołków
Działanie programu przetestujemy dla następującego grafu:

Przyjmijmy założenie, że wierzchołkiem początkowym jest wierzchołek E (o indeksie równym 4).
Oto lista sąsiedztwa tego grafu:
Przypomnijmy, w jaki sposób odczytywać listę sąsiedztwa.
Dla wierzchołka A mamy dwie listy dwuelementowe. Pierwsza to [1,1]; jej pierwszy element to indeks sąsiedniego wierzchołka, a drugi – waga krawędzi. Odczytujemy to w ten sposób, że wierzchołek A jest połączony z wierzchołkiem B krawędzią o wadze 1. Analogicznie odczytujemy kolejną parę: wierzchołek A jest połączony z wierzchołkiem C krawędzią o wadze 4.
Zapis {-1,-1} oznacza, że dany wierzchołek nie jest połączony krawędzią z wierzchołkiem o danym indeksie. W przypadku wierzchołka A widzimy, że nie ma on połączeń z kolejnymi wierzchołkami (poza B oraz C), ponieważ odpowiednie podtablice przechowują dwie wartości -1.
W naszym przypadku ważona lista sąsiedztwa (czyli lista sąsiedztwa grafu z wagami [ważonego]) jest tablicą trójwymiarową.
Zapiszemy w języku Python program, który dla danego grafu reprezentowanego przez listę sąsiedztwa wyświetlić dwie tablice: tablicę najkrótszych ścieżek do danych wierzchołków z wierzchołka początkowego oraz tablicę ich poprzedników.
Zaczniemy od utworzenia dwóch zmiennych oraz przypisania im wartości:
V– liczba wierzchołków w grafie;nieskonczonosc– zmienna służąca do inicjowania odległości w algorytmie Dijkstry; przypiszemy jej odpowiednio dużą wartość (minimalna przypisana wartość powinna być większa od iloczynu liczby wierzchołków oraz największej z wag krawędzi).
Alternatywą dla wykorzystania zmiennej nieskonczonosc jest użycie wartości maxsize, która dostępna jest po imporcie biblioteki sys.
W kolejnym kroku zapisujemy nagłówek funkcji Dijkstra. Funkcja przyjmie dwa parametry:
tablicę sąsiedztwa
listaSasiedztwa;indeks wierzchołka początkowego
s, czyli liczbę naturalną.
W programie konieczne jest zdefiniowanie tablic, z których każda pełni istotną funkcję:
d– tablica długości ścieżek (liczb naturalnych); element na pozycjiioznacza aktualną długość ścieżki prowadzącej z wierzchołka początkowego do wierzchołkai;p– tablica poprzedników najkrótszych ścieżek (liczb naturalnych); element na pozycjiioznacza indeks poprzedniego wierzchołka na najkrótszej ścieżce do wierzchołkai; za brak poprzednika uznajemy wartość-1;S– tablica wierzchołków znajdujących się w zbiorzeS(wartości logicznych); jeśli element na pozycjiijest równyTrue, oznacza to, że wierzchołekiznajduje się w zbiorzeS; domyślnie wszystkie elementy są równeFalse.
Wypełniamy tablice odpowiednimi wartościami. Wszystkie tablice będą składać się z V elementów (czyli liczby wierzchołków).
elementom tablicy
dprzypisujemy wartośćnieskonczonosc;elementom tablicy
pprzypisujemy wartość-1;elementom tablicy
Sprzypisujemy wartośćFalse.
Elementowi tablicy d o indeksie s przypisujemy wartość 0, ponieważ odległość wierzchołka początkowego do siebie samego wynosi 0.
Deklarujemy również zmienną odwiedzony – będzie ona licznikiem wierzchołków, które zostały odwiedzone. Gdy jej wartość będzie równa wartości przechowywanej przez zmienną V (całkowitą liczbę wierzchołków w grafie), będzie to oznaczać, że wszystkie wierzchołki zostały przetworzone i algorytm może zakończyć swoje działanie. Przypisujemy jej wartość 0.
Zapisujemy pętlę while, która będzie wykonywać się tak długo, aż znajdziemy najkrótsze ścieżki i poprzedników dla wszystkich wierzchołków grafu.
W pętli deklarujemy zmienną u, którą inicjalizujemy wartością -1. Zmienna ta posłuży jako flaga i będzie wskazywać, czy udało się znaleźć odpowiedni wierzchołek – taki, który nie został jeszcze odwiedzony, a jego odległość od sprawdzanego wierzchołka jest najmniejsza.
Zapisujemy pętlę for. Iterujemy przez elementy tablicy S, szukając pierwszego nieodwiedzonego wierzchołka grafu.
W pętli for zapisujemy instrukcję warunkową. By instrukcja warunkowa zwróciła wartość True, muszą zostać spełnione dwa warunki:
wierzchołek nie został jeszcze odwiedzony (
not S[i], gdzieSto tablica wartości logicznych wskazująca, czy wierzchołek został odwiedzony);uwciąż jest równe-1(co oznacza, że jest to pierwszy znaleziony nieodwiedzony wierzchołek) lub tymczasowa odległośćd[i]do tego wierzchołka jest mniejsza niż tymczasowa odległośćd[u]do wierzchołka, który znaleźliśmy wcześniej.
Jeśli oba warunki są spełnione, przypisujemy bieżący indeks i do u. Po zakończeniu tej pętli zmienna u będzie przechowywać indeks nieodwiedzonego wierzchołka o najmniejszej tymczasowej odległości lub pozostanie -1, jeśli wszystkie wierzchołki zostały już odwiedzone.
Po wybraniu wierzchołka u z najkrótszą odległością oznaczamy go jako odwiedzony. Następnie zwiększamy licznik odwiedzonych wierzchołków.
Zapisujemy pętlę for, która będzie iterować po wszystkich listach sąsiadów wierzchołka u. W tej pętli zapisujemy zagnieżdżoną pętlę for, która z kolei będzie iterować po tablicach przechowujących indeks sąsiada wierzchołka u (czyli v) oraz wagę łączących te dwa wierzchołki krawędzi (waga).
W pętli for zapisujemy instrukcję warunkową. By instrukcja warunkowa zwróciła wartość True, muszą zostać spełnione dwa warunki:
wierzchołek nie został jeszcze odwiedzony (
!S[i], gdzieSto tablica wartości logicznych wskazująca, czy wierzchołek został odwiedzony);tymczasowa odległość
d[i]do tego wierzchołka jest mniejsza niż tymczasowa odległośćd[u]do wierzchołka, który znaleźliśmy wcześniej.
Jeżeli oba te warunki są spełnione, oznacza to, że znaleźliśmy krótszą ścieżkę do wierzchołka v. Aktualizujemy więc nową odległość do wierzchołka v, która jest sumą odległości do u oraz wagi krawędzi łączącej wierzchołki u i v, a także wartość poprzednika wierzchołka v na ścieżce, którym jest wierzchołek u.
Po wykonaniu tego fragmentu kodu dla każdego wierzchołka sąsiadującego z u mamy pewność, że odległość d[v] do niego jest najkrótszą odległością, a p[v] przechowuje wierzchołek, przez który prowadzi ta najkrótsza ścieżka.
Dodajemy instrukcje odpowiedzialne za wywołanie funkcji i odpowiednie wyświetlenie wyników.
Oto kompletny kod programu:
Wynik działania programu dla analizowanego grafu:
Spróbuj ręcznie zweryfikować poprawność uzyskanego rozwiązania.
Słownik
suma wag wszystkich krawędzi, z których składa się ścieżka, droga lub cykl
graf, którego połączone krawędziami wierzchołki nie mają ustalonego kierunku, w którym można się po nich poruszać (są nieuporządkowane)
graf, w którym każda para wierzchołków połączona jest drogą
graf którego krawędzie mają przypisane wagi
nieuporządkowana para (niekoniecznie różnych) elementów zbioru wierzchołków, tj. takich, które są ze sobą połączone; w reprezentacji graficznej jest to linia łącząca te wierzchołki; krawędź może łączyć ze sobą jeden wierzchołek (wtedy nazywana jest pętlą)
zestaw V uporządkowanych list, gdzie lista o indeksie i jest listą wszystkich sąsiadów wierzchołka i
taka ścieżka łącząca dwa wierzchołki grafu, której suma wag krawędzi jest jak najmniejsza
liczba krawędzi incydentnych z danym wierzchołkiem
wartość przypisana krawędzi grafu
inaczej: punkt, węzeł; element niepustego zbioru, który wraz ze zbiorem krawędzi tworzy graf