Algorytmy i struktury danych/Algorytmy grafowe - najlżejsze ścieżki
Ten wykład poświęcimy wprowadzeniu do algorytmów grafowych. W tym celu rozwiążemy jedno następujący problem ścieżkowy.
Najlżejsze ścieżki z jednym źródłem
W tym wykładzie pisząc graf będziemy mieli zawsze na uwadze graf spójny i nieskierowany. Tradycyjnie przez będziemy oznaczali liczbę wierzchołków w grafie, a przez liczbę jego krawędzi. Dla każdego grafu mamy zawsze .
Niech będzie grafem, a funkcją przypisująca krawędziom dodatnie liczby całkowite zwane wagami krawędzi.
Ciąg wierzchołków nazywamy ścieżką w G wtedy i tylko wtedy, gdy Parser nie mógł rozpoznać (błąd składni): {\displaystyle v_i–v_{i+1}} jest krawędzią w , dla każdego . Liczbę nazywamy długością ścieżki .
Wagą ścieżki nazywamy sumę wag jej krawędzi i oznaczamy także przez . Przyjmujemy, że waga ścieżki o długości 0 wynosi 0.
Przez będziemy oznaczali zbiór sąsiadów wierzchołka w grafie .
Problem (wag) najlżejszych ścieżek z jednym źródłem definiuje się nastepująco.
Dane: – spójny, nieskierowany graf – funkcja przypisująca krawędziom dodatnie liczby całkowite – wyróżniony wierzchołek w G, zwany źródłem Wynik: dla każdego wierzchołka waga najlżejszej ścieżki łączącej v z s.
W tym wykładzie zajmiemy się tylko wyznaczaniem wag najlżejszych ścieżek. Znalezienie sposobu wyznaczania takich ścieżek pozstawimy jako ćwiczenie dla czytelników. Problem najlżejszych scieżek jest problemem bardzo naturalnym. Jeśli mapę drogową potraktować jako graf, w którym wagi to długości odcinków drogowych na mapie, a źródło jest wyróżnionym miastem na mapie (na przykład stolicą państwa), to rozwiązanie problemu najlżejszych ścieżek da nam długości najkrótszych dróg od miasta-źródła do pozostałych miast na mapie.
Znaczenie problemu najlżejszych ścieżek jest dużo ważniejsze niż by to wynikało z jego zastosowań. Poszukując wydajnego algorytmu dla rozwiązania tego problemu postaramy się przedstawić cały proces projektowania, a następnie implementowania algorytmu ze szczegtólnym uwzględnieniem doboru odpowiednich struktur danych w celu osiągnięcia lepszej wydajności implementacji.
Nasze rozważania rozpoczniemy od schematu algorytmu poszukiwania najlżejszych ścieżek, a następnie zajmiemy się implementacją poszczególnych elementów tego schematu. Rozwiązanie, które zaproponujemy pochodzi od wybitnego holenderskiego informatyka Edgara Dijkstry.
Zastanówmy się w jaki sposób obliczać wagi najlżejszych ścieżek. Pomoże nam w tym następujace proste spostrzeżenie:
Niech będzie najlżejszą ścieżką z wierzchołka , do źródła . Wówczas .
Spostrzeżenie to mówi, że gdybyśmy obliczali wagi najlżejszych ścieżek od najlżejszej do najcięższej, to dla każdego wierzchołka różnego od źródła, sąsiad na najlżejszej ścieżce z do miałby swoją wagę już obliczoną i tym sąsiadem byłby wierzchołek (może być wiecej niż jeden wybór, dla którego waga najlżejszej ścieżki do powiększona o wagę krawędzi łączącej go z jest najmniejsza.
Możemy teraz przystąpić do schematu algorytmu. Algorytm obliczania najlżejszych ścieżek będzie działał w fazach. W trakcie działania algorytmu będzie zbiorem tych wierzchołków dla których wagi najlżejszych scieżek prowadzące do zostały już policzone. Przez będziemy oznaczali zbiór pozostałych wierzchołków. Podczas obliczeń z kazdym wierzchołkiem będziemy mieli związana nieujemną wagę . Dla każdego wierzchołka waga w będzie wagą najlżejszej scieżki łączacej z , czyli . Dla każdego wierzchołka z , będzie równe wadze najlżejszej ścieżki z do , na której wszystkie wierzchołki poza v muszą należeć do . Może się zdarzyć, że taka ścieżka nie istnieje. Wówczas przyjmujemy, że jest równe - umownej wartości większej od wszystkich wag występujących w obliczeniach. Jedna faza algorytmu będzie polegała na przeniesieniu do wierzchołka z o najmniejszej wadze , a nstępnie poprawieniu wag dla wierzchołków, które pozostały w . Zauważmy, że wagi mogą się zmienić tylko sąsiadom w grafie wierzchołka przeniesionego do , którzy pozostali w . Oto schemat algorytmu obliczania najlżejszych ścieżek. Nazwiemy go schematem Dijkstry.
Algorytm Schemat Dijkstry
1 //Inicjacja 2 ; ; 3 ; 4 for each v \in R do 5 if then 6 7 else 8 ; 9 //właściwe obliczenia 10 for to do 11 begin 12 wierzchołek z o minimalnym ; 13 ; 14 ; 15 for each do 16 if then 17 ; 18 end; 20 //dla każdego ,
Dlaczego powyższy opis obliczania wag najlzejszych ścieżek nazwaliśmy schematem Dijkstry, a nie algorytmem Dijkstry. Zauważmy, że żeby powyższy schemat móc zaimplementować na komputerze, a także dokonać analizy złożoności obliczeniowej musimy doprecyzować wiele elementów schematu. Należą do nich
- implementacja grafu - implementacja zbiorów L i P i operacji na nich (inicjacja, usuwanie i dodawanie wierzchołka, znajdowanie wierzchołka o minimalnej wadze, zmiana wagi wierzchołka).
Dopiero scisłe określenie tych elementów da nam pełną implementację schematu, a tym samym algorytm, który będzie mógł być poddany analizie.