Algorytm Floyda-Warshalla pozwala znaleźć dla grafu skierowanego z pewnym wagami krawędzi znaleźć najkrótszą cieżkę pomiędzy dwom wybranymi wierzchołkami przechodząc pomiędzy nimi k innych wierzchołków. Wagi ujemne krawędzi są możliwe, ale pod warunkiem, że graf nie ma ujemnych cykli (wtedy wyniki są niepoprawne).
Algorytm rozpoczyna się od utworzenia pustej macierzy n×n w której zostaną zapisane dane początkowe. Jeśli od wierzchołka i istnieje krawędź do j to na pozycji (i, j) należy zapisać wartość krawędzi oraz poprzednika i. Jeśli jednak dana krawędź nie istnieje to odległość należy ustalić na nieskończoność, a poprzednika na nieustawionego. W rezultacie otrzymuje się macierz najkrótszych ścieżek dla k = 0 wierzchołków pośrednich. Macierz ta będzie identyczna z macierzą wejściową.
Tworzenie kolejnej macierzy k będzie pobierać dane z macierzy poprzedniej k-1. Dla każdego połączenia (i, j) sprawdzane jest czy suma dystansu (i, k) oraz (k, j) jest mniejsza od dystansu zapisanego w polu (i, j). Jeśli warunek jest spełniony to krawędź (i, j) zostaje ustalona na obliczoną sumę, a poprzednik na k. W przypadku, gdy warunek nie jest spełniony to należy skopiować dystans oraz poprzednika.
Dla przykładu na poniższym grafie zostanie wykonany algorytm Floyda-Warshalla:
Macierz k = 0 będzie wyglądać następująco:
- | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
0 | - | - | - | 3 | 0 | - |
1 | 3 | 1 | - | 4 | 1 | 1 | 1 | - |
2 | - | -1 | 2 | - | 2 | 2 | - |
3 | -2 | 3 | 5 | 3 | - | - | 2 | 3 |
4 | - | - | 1 | 4 | - | - |
Macierz k = 1 nie zmienia się. Dla żadnej prawy wybranych wierzchołków nie został spełniony warunek:
- | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
0 | - | - | - | 3 | 0 | - |
1 | 3 | 1 | - | 4 | 1 | 1 | 1 | - |
2 | - | -1 | 2 | - | 2 | 2 | - |
3 | -2 | 3 | 5 | 3 | - | - | 2 | 3 |
4 | - | - | 1 | 4 | - | - |
Sytuacja zmienia sie dla macierzy k = 2, ponieważ zaktualizowane zostają puste dotychczas wartości dla wierzchołka 3. Jak można zauważyć na grafie jest to wierzchołek najlepiej skomunikowany z innymi. Przykładowo wartość pomiędzy (3, 2) zostaje zaktualizowana, ponieważ:
(3, 1) + (1, 2) = 5 + 4 = 9 co jest mniejsze niż dotychczasowa wartość nieskończoność na tym polu
(3, 1) + (1, 3) = 1 + 5 = 9 co też jest mniejsze niż nieskończoność
Kolejna macierz k = 2 ostatecznie wygląda następująco:
- | 0 | 1 | 2 | 3 | 4 |
---|---|---|---|---|---|
0 | - | - | - | 3 | 0 | - |
1 | 3 | 1 | - | 4 | 1 | 1 | 1 | - |
2 | - | -1 | 2 | - | 2 | 2 | - |
3 | -2 | 3 | 5 | 3 | 9 | 1 | 6 | 1 | 2 | 3 |
4 | - | - | 1 | 4 | - | - |
Z tej macierzy możemy odczytać, że chcąc przejść od wierzchołka 3 z powrotem do 3 najkrótsza droga wynosi 6 i prowadzi ona od wierzchołka 3 poprzez 1 oraz 0 z powrotem do wierzchołka 3. Obliczenia kolejnych macierzy można kontynuować, aż do macierz k = 5.
Funkcja FloydWarshall() dla podanego grafu zwróci tablicę macierzy, gdzie q-tej pozycji będzie macierz dla k = q. Dla każdego pola macierzy będzie można odczytać dystans do wierzchołka oraz poprzednika. Algorytm nie będzie sprawdzał poprawności danych wejściowych - o tym powinien wiedzieć sam użytkownik. Graf ma zostać przekazany jako macierz. Przykładowo następujący graf:
ma zostać przekazany w programie jako następująca macierz:
W celu przechowania danych utworzony zostaje specjalny typ, który dla wybranego wierzchołka pozwoli przechować jego dystans oraz poprzednika.
W trakcie działania algorytmu będą dodawane bardzo różne wartości. W założeniu największa wartość zmiennej to nieskończoność, ale komputer będzie traktował to jako zwykłą liczbę i próbował dodać te dwie wartości. Z tego powodu potrzebna jest specjalna funkcja dodawania dwóch wartości, że jeśli choć jedna z nich jest nieskończonością to na pewno wynikiem będzie nieskończoność.
Funkcja FloydWarshall() dla podanej macierzy danych szuka k kolejnych macierz, które będą wyznaczać najkrótsze ścieżki o k pośrednich wierzchołkach.
(2. - 7.) Deklaracja struktury danych do zapisu informacji. Jest to trójwymiarowa tablica, gdzie pierwszy wymiar to numer macierzy, drugi to numer wiersza, a ostatni kolumny. Następnie (9. - 18.) tworzona jest macierz k = 0 od której zależeć będą dalsze obliczenia. Dalej (20. - 33.) wypełniane są kolejne macierze na podstawie poprzednich. Na koniec (35.) zwrócony zostaje wynik.
W celu przetestowania kodu można skorzystać z poniższego fragmentu kodu, który wczyta od użytkownika potrzebne dane, a na koniec wypisze wszystkie obliczone macierze: