Floyd-Warshallův algoritmus (Floydův algoritmus) slouží k nalezení nejkratších cest mezi všemi dvojicemi uzlů grafu, který neobsahuje cykly záporné délky. Jeho hlavní výhodou je jeho implementační jednoduchost.
Princip
Floyd-Warshallův algoritmus má na vstupu matici délek, říkejme jí . Pokud mezi dvěma uzly vede hrana délky l, tak tato matice obsahuje na indexu právě tuto hodnotu. Na diagonále má tato matice samé nuly a na ostatních indexech, které neodpovídají hraně nekonečno. Jinými slovy tato matice obsahuje vzálenosti uzlů, které nevedou skrze žádného prostředníka.
V každé iteraci Floyd-Washallova algoritmu se tato matice přepočítá tak, aby vyjadřovala vzdálenost všech dvojic uzlů skrze postupně se zvětšující množinu přípustných prostředníků. Jednoduše řečeno matice bude vyjadřovat vzdálenost všech uzlů s možností využití jednoho (daného) prostředníka, vzdálenost při možném využití dvou (daných) mezilehlých uzlů, při možnosti využití m mezilehlých uzlů.
Tato transformace se dá vyjádřit pro následujícím rekurentním vztahem:
Protože při trasformaci nemůže dojít k přepisu hodnot, z nichž se počítají prvky nové matice, tak v samotné implementaci stačí použít právě jednu matici pro a .
Kód
procedure [array] FloydWarshall(D, P) for k in 1 to n do for i in 1 to n do for j in 1 to n do if D[i][j] > D[i][k] + D[k][j] then D[i][j] = D[i][k] + D[k][j] P[i][j] = P[k][j] return P
/** * Floyd-Warshall algorithm. Finds all shortest paths among all pairs of nodes * @param d matrix of distances (Integer.MAX_VALUE represents positive infinity) * @return matrix of predecessors */ public static int[][] floydWarshall(int[][] d) { int[][] p = constructInitialMatixOfPredecessors(d); for (int k = 0; k < d.length; k++) { for (int i = 0; i < d.length; i++) { for (int j = 0; j < d.length; j++) { if (d[i][k] == Integer.MAX_VALUE || d[k][j] == Integer.MAX_VALUE) { continue; } if (d[i][j] > d[i][k] + d[k][j]) { d[i][j] = d[i][k] + d[k][j]; p[i][j] = p[k][j]; } } } } return p; } /** * Constructs matrix P0 * @param d matrix of lengths * @return P0 */ private static int[][] constructInitialMatixOfPredecessors(int[][] d) { int[][] p = new int[d.length][d.length]; for (int i = 0; i < d.length; i++) { for (int j = 0; j < d.length; j++) { if (d[i][j] != 0 && d[i][j] != Integer.MAX_VALUE) { p[i][j] = i; } else { p[i][j] = -1; } } } return p; }
Složitost
Protože má minimalizace ve vnitřním cyklu konstantní složitost, tak je zjevné, že je celková asymptotická složitost algoritmu .
Příklad
Matice předchůdců
Aby bylo možné zrekonstruovat cesty mezi jednotlivými uzly, tak Floyd-Warshallův algoritmus generuje také matici předchůdců. Tuto matici, která je výstupem algoritmu, čteme následujícím způsobem: pokud hledáme cestu mezi uzly , pak se podíváme na záznam na řádek , sloupec . Pokud je pole nulové, pak cesta neexistuje, v opačném případě udává předchůdce koncového uzlu na této cestě. Tento postup rekurzivně opakujeme tak dlouho, dokud není předchůdce roven počátečnímu uzlu .
Čtení cesty z matice předchůdců
function getPath(p, i, j) if i == j then write(i) else if p[i][j] = 0 then write("Cesta neexistuje") else getPath(p, i, p[i][j]) write(j)
Příklad
Detekce cyklů (ne)záporné délky
Za předpokladu, že se na diagonálu matice délek umístí místo nul hodnoty nekonečno, tak algoritmus nalezne cykly (ne)záporné délky, jsou-li v grafu obsaženy, tzn. na diagonále matice předchůdců nebudou obsaženy nuly.
Literatura
- KOLÁŘ, Josef. Teoretická informatika. 2. vyd. Praha : Česká informatická společnost, 2004. 205 s. ISBN 80-900853-8-5.
- HANZÁLEK, Zdeněk. Kombinatorická optimalice, Nejkratší cesty - slides k přednáškám.