Wyszukiwanie najkrótszej drogi między parą wierzchołków w grafie
Problem
Aby znaleźć drogę do celu musimy drogę zaplanować. Możemy wyszukać najszybszą, najtańszą, ekonomiczną. A jak sprawdzić która to która? Mamy wiele algorytmów pomagających nam to obliczyć i jednym z nich jest algorytm Floyda-Warshalla. Algorytm ten pozwala odnaleźć najkrótszą drogę między dwoma wierzchołkami w grafie ważonym.
Jak działa algorytm?
W grafie jeżeli najkrótsza droga między wierzchołkami a i c prowadzi przez wierzchołek b to znaczy, że jest ona połączeniem najkrótszych dróg między a i b oraz b i c.
Na początku algorytmu definiowana jest tablica najkrótszych ścieżek, tak że dla każdej pary wierzchołków mamy:
- 0 gdy a = c (ten sam wierzchołek)
- w(a, c) gdy istnieje krawędź między a i c
- nieskończoność gdy nie istnieje krawędź między a i c
Złożoność algorytmu
Implementacja algorytmu
Sposobem przechowywania grafu w pamięci komputera są różne struktury danych. Jedną z nich jest macierz. Za pomocą macierzy możemy opisać które wierzchołki sąsiadują z innymi.
Przykładowa macierz może wyglądać następująco:

A graf który przedstawia to:

Do opisu grafów ważonych wykorzystuje się również macierz wag.


W implementacji wykorzystamy graf opisany macierzą:

Graf będzie się przedstawiał następująco:

using System;
using System.Collections.Generic;
namespace FloydWarshall
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Przykładowa implementacja algorytmu Floyda-Warshalla");
//przygotowanie danych
//utworzenie tablicy
int rozmiar_tablicy = 5; //ilosc wierzchołków grafu
var tablica = new int[rozmiar_tablicy, rozmiar_tablicy];
//wypełnienie tablicy
for (int i = 0; i < rozmiar_tablicy; i++)
for (int j = 0; j < rozmiar_tablicy; j++)
{
tablica[i, j] = Floyd.INFINITI;
}
//uzupełnienie macierzy sąsiedztwa
//tablica zaharkodowana, najlepiej by było przekazywać macierz jako parametr
tablica[0, 1] = 2;
tablica[0, 2] = 3;
tablica[1, 3] = 3;
tablica[1, 4] = 7;
tablica[2, 3] = 4;
tablica[3, 4] = 1;
//utworzenie instancji klasy
var floyd = new Floyd();
//wyliczenie odległości między wierzchołkami
floyd.Evaluate(rozmiar_tablicy, tablica);
//pobranie ścieżki między wierzchołkami podanymi w parametrze metody
var test = floyd.GetPath(0, 4);
foreach (var item in test)
{
Console.WriteLine(item.ToString());
};
Console.ReadLine();
}
}
class Floyd
{
//stała - nieskończoność
public const int INFINITI = int.MaxValue;
//tymczasowa zmienna, podczas iteracji przechowuje wartosc obliczaną wg algorytmu
int[,] nastepnik;
//ścieżka
int[,] sciezka;
//wyliczenie odległości między wierzchołkami w grafie
public void Evaluate(int rozmiar_tablicy, int[,] sciezka)
{
this.sciezka = sciezka;
nastepnik = new int[rozmiar_tablicy, rozmiar_tablicy];
for (int i = 0; i < rozmiar_tablicy; i++)
for (int j = 0; j < rozmiar_tablicy; j++)
if (sciezka[i, j] == INFINITI)
nastepnik[i, j] = INFINITI;
else
nastepnik[i, j] = i;
for (int k = 0; k < rozmiar_tablicy; k++)
for (int i = 0; i < rozmiar_tablicy; i++)
for (int j = 0; j < rozmiar_tablicy; j++)
if (sciezka[i, k] != INFINITI && sciezka[k, j] != INFINITI &&
sciezka[i, k] + sciezka[k, j] < sciezka[i, j])
{
sciezka[i, j] = sciezka[i, k] + sciezka[k, j];
nastepnik[i, j] = k;
}
//sprawdzenie czy nie ma ujemnych ścieżek
for (int i = 0; i < rozmiar_tablicy; i++)
if (sciezka[i, i] < 0)
throw new ArgumentException("Negative cycles");
}
//pobranie najkrótszej ścieżki
public List<int> GetPath(int start, int meta)
{
var path = new List<int>();
if (nastepnik == null)
return null;
if (nastepnik[start, meta] == INFINITI)
return new List<int>();
if (start == meta)
return new List<int>() { start };
getPathRekonstruktor(path, start, meta);
path.Add(meta);
return path;
}
//metoda rekursywna do pobierania ścieżki między węzłami
private void getPathRekonstruktor(List<int> sciezka, int start, int meta)
{
int posredni = nastepnik[start, meta];
if (posredni == start)
{
sciezka.Add(start);
return;
}
getPathRekonstruktor(sciezka, start, posredni);
getPathRekonstruktor(sciezka, posredni, meta);
}
}
}

Pingback:Algorytm Dijkstry (w trakcie pisania) – Notes informatyka