TGH06 - Hledání nejkratší cesty Jan Březina Technical University of Liberec 26. března 2013
Motivační problémy Silniční sít reprezentovaná grafem. Najdi nejkratší/nejrychlejší cestu z místa A do místa B. Najdi nejrychlejší dopravní spojení (IDOS). Routovaní. Nejkratší cesta pro pakety TCP/IP spojení. AI (pathfinding) pro roboty a boty (ohodnocení grafu může zahrnovat i nebezpečnost cesty) Hledání nejširší cesty (přeprava rozměrných nákladů).
Reálné problémy - hledání nejkratší cesty Hledání nejkratší nebo nejrychlejší cesty na mapě. (Rovinný graf) IDOS. Nejrychlejší dopravní spojení. (DAG) Hledání spojení s maximální propustností. (bottleneck path) Path density. Importance of people in social networks.
Path density
Grafové formulace Vstup: Graf (vrcholu, hrany), orientovaný Ohodnocení hran (nezáporné) Shortest path: Najdi nejkratší cestu z vrcholu A do vrcholu B. Single source shortest path: Najdi nejkratší cesty z vrcholu S do všech vrcholů. V single source úloze je řešením strom nejkratších cest (z každého vrcholu kromě S vede právě jedna hrana směrem k S, tj. N 1 hran). All-to-all shortest path: Najdi nejkratší cestu pro všechny dvojice vrcholů.
Princip optimality Pokud X Z Y je nejkratší cesta z X do Y, pak X Z a Z Y jsou také nejkratší cesty. Důsledek:
Modifikace BFS Pro celočíselná ohodnocení bychom mohli hrany rozdělit na podhrany.
Modifikace BFS Pro celočíselná ohodnocení bychom mohli hrany rozdělit na podhrany. Spustíme BFS z vrcholu S. Z vrcholu S vypustíme Číňany. Ve vrcholech se budou dělit.
Modifikace BFS Pro celočíselná ohodnocení bychom mohli hrany rozdělit na podhrany. Spustíme BFS z vrcholu S. Z vrcholu S vypustíme Číňany. Ve vrcholech se budou dělit. Pro velká ohodnocení budeme opakovaně dávat do fronty vrcholy na stejných makro hranách. Číňané pouze jdou.
Modifikace BFS Pro celočíselná ohodnocení bychom mohli hrany rozdělit na podhrany. Spustíme BFS z vrcholu S. Z vrcholu S vypustíme Číňany. Ve vrcholech se budou dělit. Pro velká ohodnocení budeme opakovaně dávat do fronty vrcholy na stejných makro hranách. Číňané pouze jdou. Chceme zjistit, který Čínan první dorazí na nějakou křižovatku. Pro něj provedeme rozdělení.
Modifikace BFS Pro celočíselná ohodnocení bychom mohli hrany rozdělit na podhrany. Spustíme BFS z vrcholu S. Z vrcholu S vypustíme Číňany. Ve vrcholech se budou dělit. Pro velká ohodnocení budeme opakovaně dávat do fronty vrcholy na stejných makro hranách. Číňané pouze jdou. Chceme zjistit, který Čínan první dorazí na nějakou křižovatku. Pro něj provedeme rozdělení. Lépe: Na kterou křižovatku dorazí prvně nějaký Číňan. Křižovatek (vrcholů) je méně než skupin Číňanů (hran).
Prioritní fronta Datová struktura s operacemi: Odeber Minum (AcessMin, DeleteMin) - vrat prvek s minimálním kĺıčem a odeber ho z fronty Zmenši Kĺıč (DecreaseKey) - zmenši kĺıč daného prvku ve frontě Naplnění fronty - vytvoření fronty z dané množiny prvků, nebo operace přidání prvku Možno implementovat pomocí haldy. Pro binární haldu máme složitost prvních dvou operací O(log n).
Dijkstra s algorithm Vstup: vrcholy V a sousednosti Adj[u] = {e E e(u, v)} s ohodnocením w(e), počáteční vrchol s Výstup: vzdálenosti d[i] od s, předci π[i] stromu minimálních vzdáleností for u V do d[u] = ; d[s] = 0; π[s] = NULL; Naplň prioritní frontu Q vrcholy V s prioritami d[ ]. while u = OdeberMin(Q) do for e = (u, v) Adj[u] do alt = d[u] + w(e); if alt < d[v] then π[v] = u; d[v] = alt; ZmenšiKĺıč(Q,v);
Složitost Dijkstra Obecně: Pro seznam: O( V (čas OdeberMin) + E (čas ZmenšiKĺıč)) O( V O( V ) + E O(1)) = O( V 2 ) Pokud E V 2 je toto optimální, jinak se vyplatí použít: Binární haldu: O(( V + E ) log V ) nebo dokonce Fibonacciho haldu: O( V log V + E )
Správnost Dijkstra Theorem Necht R je množina vrcholů mimo prioritní frontu. V průběhu celého algoritmu platí d[v] = δ(v) := δ(s, v) pro všechny vrcholy v R. Důkaz indukcí. Na počátku je R =, tvrzení platí. Pokud platí pro R, platí i po přidání vrcholu v, tj. pro R {v}. Sporem: Existuje cesta P = (s,..., x, y,..., v) kratší než d[v], Necht, (x, y), x R a y / R, je hrana nejbĺıže s (existuje, protože s R a v / R. Vrchol v má nejmenší d[ ] z vrcholů mimo R. Tedy d[v] d[y]. Při přidání x do R jsme prošli i hranu (x, y), je tedy: d[x] + w(x, y) d[y] a zároveň d[x] = δ(x) podle indukčního předpokladu. Dohromady: w(p ) δ(x) + w(x, y) d[v] takže cesta P není kratší.
Bottleneck path Problém: Najdi cestu z města A do města B s největší minimální šířku silnice. Modifikace Dijkstra: vrcholy - města hrany - silnice váhy hran - šířky silnic délka cesty - minum z hodnot hran (nejužší místo na cestě) fronta - maximální, d[ ] šířka nejširší cesty while u = OdeberMax(Q) do for e = (u, v) Adj[u] do alt = min(d[u], w(e)); if alt > d[v] then π[v] = u; d[v] = alt; ZvětšKĺıč(Q,v);
Bellman-Ford pro záporné váhy for u V do d[u] = ; d[s] = 0; π[s] = NULL; for i = 1,..., V 1 do for e = (u, v) E do alt = d[u] + w(e); if alt < d[v] then π[v] = u; d[v] = alt; for e = (u, v) E do u = e.begin; v = e.end; alt = d[u] + w(e); if alt < d[v] then Error: negative cycle.; princip: V kroku i najdu všechny nejkratší cesty délky i. složitost: O( V E )
Floyd Warshall - všechny nejkratší cesty Počáteční hodnota d[i][j] je hodnota příslušné hrany, nebo nekonečno pokud hrana neexistuje. middle[ ][ ] = NULL for k = 1,..., V do for i = 1,..., V do for j = 1,..., V do if d[i][k] + d[k][j] < d[i][j] then d[i][j] = d[i][k] + d[k][j]; middle[i][j] = k; Pro každé k se aktualizuje d[i][j], což je nejkratší cesta z i do j s použitím vrcholů 1,..., k. middle[i][j] udává mezivrchol s nejvyšším indexem na nejkratší cestě z i do j. Libovolnou nejkratší cestu i j je možno rekurzivně rekonstruovat jako: i middle[i][j], middle[i][j] j. Při přítomnosti záporných cyklů jsou na diagonále matice d záporná čísla.
Dynamic programming
A algorithm Dijkstrův algoritmus + heuristika vzdálenosti do cíle c. Přípustná (admisible) heuristika h(v): pro každý vrchol v máme navíc spodní odhad nejkratší vzdálenosti, t.j. monotóní heuristika δ(v, c) h(v) h(x) δ(x, y) + h(y) V Dijkstrově algoritmu pak modifikujeme ceny hran na: FINISH d (u, v) = d(u, v) h(u) + h(v)
A* algorithm, monotonni for u V do d[u] = ; d[s] = 0; π[s] = NULL; Naplň prioritní frontu Q vrcholy V s prioritami d[ ]. while u = OdeberMin(Q) do for e = (u, v) Adj[u] do alt = d[u] + w(e) h[u] + h[v]; if alt < d[v] then π[v] = u; d[v] = alt; ZmenšiKĺıč(Q,v); Stačí modifikovat ohodnocení hran. Díky monotonii nevznikaji zaporné hrany a každý vrchol se do fronty dostane maximálně jednou.
A* algorithm, monotonni for u V do d[u] = ; d[s] = 0; π[s] = NULL; Naplň prioritní frontu Q vrcholy V s prioritami d[ ]. while u = OdeberMin(Q) do for e = (u, v) Adj[u] do alt = d[u] + w(e); if alt < d[v] then π[v] = u; d[v] = alt; ZmenšiKĺıč(Q,v, d[v] + h[v]); Ohodnocení i hodnoty d[:] jsou jako v Dijkstrovi, ale priority ve frontě jsou modifikované. Pro monoténní heuristiku je to ekvivalentní předchozímu přístupu. Jinak se vrcholy mohou do fronty vracet. Jakou to pak má složitost??
Dálniční hierarchie