From: Martin Mares Date: Sat, 16 Oct 2010 15:05:27 +0000 (+0200) Subject: Prvni cast nove kapitoly o nejkratsich cestach X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=154f2a5f8a02ad2170e1100fe672031247ebeca9;p=ga.git Prvni cast nove kapitoly o nejkratsich cestach --- diff --git a/13-dijkstra/13-dijkstra.tex b/13-dijkstra/13-dijkstra.tex new file mode 100644 index 0000000..8673f99 --- /dev/null +++ b/13-dijkstra/13-dijkstra.tex @@ -0,0 +1,277 @@ +\input ../sgr.tex + +\prednaska{13}{Nejkrat¹í cesty: relaxaèní metody}{} + +\def\ppsp{1-1} +\def\sssp{1-$n$} +\def\apsp{$n$-$n$} + +Problém hledání nejkrat¹í cesty v~(obvykle ohodnoceném orientovaném) grafu +provází teorii grafových algoritmù od~samých poèátkù. Základní algoritmy +pro hledání cest jsou nedílnou souèástí základních kursù programování +a algoritmù, my se budeme vìnovat zejména rùzným jejich vylep¹ením. + +Mìjme tedy nìjaký orientovaný graf, jeho¾ ka¾dá hrana~$e$ je opatøena +{\I délkou} $\ell(e)$, co¾ je nìjaké reálné èíslo. Pro libovolnou +posloupnost hran~$P$ (speciálnì tedy pro sled nebo cestu) definujeme +její délku $\ell(P)$ jako souèet délek v¹ech hran posloupnosti. +Za {\I vzdálenost} $d(u,v)$ dvou vrcholù prohlásíme nejmen¹í mo¾nou +délku cesty z~$u$ do~$v$ (jeliko¾ cest je v~grafu koneènì mnoho, +minimum v¾dy existuje). Pokud z~$u$ do~$v$ ¾ádná cesta nevede, polo¾íme +$d(u,v) := \infty$. + +Obvykle se studují následující tøi varianty problému: + +\itemize\ibull +\:{\bo 1-1} neboli {\bo P2PSP} (Point to Point Shortest Path) -- chceme nalézt nejkrat¹í + ze~v¹ech cest z~daného vrcholu~$u$ do~daného vrcholu~$v$. +\:{\bo 1-n} neboli {\bo SSSP} (Single Source Shortest Paths) -- pro daný vrchol~$u$ chceme + nalézt nejkrat¹í cesty do~v¹ech ostatních vrcholù. +\:{\bo n-n} neboli {\bo APSP} (All Pairs Shortest Paths) -- zajímají nás nejkrat¹í cesty + mezi v¹emi dvojicemi vrcholù. +\endlist + +Pøekvapivì, tak obecnì, jak jsme si uvedené problémy definovali, je neumíme +øe¹it v~polynomiálním èase: pro grafy, které mohou obsahovat hrany záporných +délek bez jakýchkoliv omezení, je toti¾ hledání nejkrat¹í cesty NP-tì¾ké +(lze na~nìj snadno pøevést existence hamiltonovské cesty). V¹echny známé +polynomiální algoritmy toti¾ místo nejkrat¹í cesty hledají nejkrat¹í sled +-- nekontrolují toti¾ nijak, zda cesta neprojde jedním vrcholem vícekrát. + +Na¹tìstí pro nás je to v~grafech bez cyklù záporné délky toté¾: pokud se +v~nalezeném sledu vyskytne cyklus, mù¾eme jej \uv{vystøihnout} a tím získat +sled, který není del¹í, a~který má ménì hran. Ka¾dý nejkrat¹í sled tedy +mù¾eme upravit na~stejnì dlouhou cestu. +V~grafech bez záporných cyklù je tedy jedno, zda hledáme sled nebo cestu; +naopak vyskytne-li se záporný cyklus dosa¾itelný z~poèáteèního vrcholu, +nejkrat¹í sled ani neexistuje. + +Navíc se nám bude hodit, ¾e ka¾dý prefix nejkrat¹í cesty je opìt nejkrat¹í +cesta. Jinými slovy pokud nìkterá z~nejkrat¹ích cest z~$u$ do~$v$ vede +pøes nìjaký vrchol~$w$, pak její èást z~$u$ do~$w$ je jednou z~nejkrat¹ích +cest z~$u$ do~$w$. (V~opaèném pøípadì bychom mohli úsek $u\ldots w$ vymìnit za~krat¹í +cestu.) + +Díky této vlastnosti mù¾eme pro ka¾dý vrchol~$u$ sestrojit jeho {\I strom +nejkrat¹ích cest}~${\cal T}(u)$. To je strom definovaný na~vrcholech zadaného +grafu~$G$, zakoøenìný v~$u$ a orientovaný smìrem od~koøene, takový, ¾e pro ka¾dý +vrchol~$v$ je (jediná) cesta z~$u$ do~$v$ v~tomto stromu jednou z~nejkrat¹ích +cest z~$u$ do~$v$ v~grafy~$G$. + +\>{\bo Pozorování:} Strom nejkrat¹ích cest v¾dy existuje. + +\proof +Nech» $u=v_1,\ldots,v_n$ jsou v¹echny vrcholy grafu~$G$. Indukcí budeme +dokazovat, ¾e pro ka¾dé~$i$ existuje strom~${\cal T}_i$, v~nìm¾ se nacházejí nejkrat¹í +cesty z~vrcholu~$u$ do vrcholù $v_1,\ldots,v_i$. Pro $i=1$ staèí uvá¾it +strom obsahující pouze vrchol~$u$. Ze~stromu ${\cal T}_{i-1}$ pak vyrobíme +strom ${\cal T}_i$ takto: Nalezneme v~$G$ nejkrat¹í cestu z~$u$ do~$v_i$ +a oznaèíme~$z$ poslední vrchol na~této cestì, který se je¹tì vyskytuje v~${\cal T}_{i-1}$. +Úsek nejkrat¹í cesty od~$z$ do~$v_i$ pak pøidáme do~${\cal T}_{i-1}$ +a díky prefixové vlastnosti bude i cesta z~$u$ do~$v_i$ v~novém stromu +nejkrat¹í. +\qed + +Zbývá se dohodnout, v~jakém tvaru mají na¹e algoritmy vydávat výsledek. +U~problémù typu \ppsp{} je nejjednodu¹¹í vypsat celou cestu, u~\sssp{} mù¾eme jako výstup +vydat strom nejkrat¹ích cest z~daného poèátku (v¹imnìte si, ¾e staèí +uvést pøedchùdce ka¾dého vrcholu), u~\apsp{} vydáme strom nejkrat¹ích cest +pro ka¾dý ze~zdrojových vrcholù. + +Èasto se ov¹em uká¾e, ¾e podstatná èást problému se skrývá v~samotném +výpoètu vzdáleností a sestrojení pøedchùdcù je triviálním roz¹íøením algoritmu. +Budeme tedy obvykle jen poèítat vzdálenosti a samotnou rekonstrukci cest +ponecháme ètenáøi jako snadné cvièení. + +\h{Relaxaèní algoritmus} + +Zaènìme problémem \sssp{} a oznaème~$u$ výchozí vrchol. Vìt¹ina známých +algoritmù funguje tak, ¾e pro ka¾dý vrchol~$v$ udr¾ují ohodnocení $h(v)$, +které v~ka¾dém okam¾iku odpovídá délce nìjakého sledu z~$u$ do~$v$. Postupnì +toto ohodnocení upravují, a¾ se z~nìj stane vzdálenost $d(u,v)$ a algoritmus +se mù¾e zastavit. + +Vhodnou operací pro vylep¹ování ohodnocení je takzvaná {\I relaxace.} +Vybereme si nìjaký vrchol~$v$ a pro v¹echny jeho sousedy~$w$ spoèítáme +$h(v) + \ell(v,w)$, tedy délku sledu, který vznikne roz¹íøením aktuálního +sledu do~$v$ o~hranu $(v,w)$. Pokud je tato hodnota men¹í ne¾~$h(w)$, +tak jí $h(w)$ pøepí¹eme. + +Relaxace tedy zachovává to, ¾e ohodnocení odpovídá délkám nìjakých sledù, +a~souèasnì toto ohodnocení mù¾e zlep¹ovat. Budeme se tedy sna¾it nìjaké +poèáteèní ohodnocení (nabízí se $h(u)=0$, $h(v)=\infty$ pro $v\ne u$) +postupnými relaxacemi pøetvoøit na~ohodnocení vzdálenostmi. + +Abychom zabránili opakovaným relaxacím tého¾ vrcholu, které nic nezmìní, +budeme rozli¹ovat tøi stavy vrcholù: {\I nevidìn} (je¹tì jsme ho nenav¹tívili), +{\I otevøen} (zmìnilo se ohodnocení, èasem chceme relaxovat) a {\I uzavøen} +(u¾ jsme relaxovali a není potøeba znovu). + +Ná¹ algoritmus bude fungovat následovnì: + +\algo +\:$h(*)\leftarrow \infty$, $h(u)\leftarrow 0$. +\:$\(*)\leftarrow\$, $\(u)\leftarrow\$. +\:Dokud existují otevøené vrcholy, opakujeme: +\::$v\leftarrow\hbox{libovolný otevøený vrchol}$. +\::$\(v)\leftarrow\$. +\::Relaxujeme~$v$: +\:::Pro v¹echny hrany $(v,w)$ opakujeme: +\::::Je-li $h(w) < h(v) + \ell(v,w)$: +\:::::$h(w)\leftarrow h(v) + \ell(v,w)$. +\:::::$\(w)\leftarrow\$. +\:Vrátíme výsledek $d(u,v)=h(v)$ pro v¹echna~$v$. +\endalgo + +Podobnì jako u~minimálních koster, i zde se jedná o~meta-algoritmus, proto¾e +v~kroku~4 nespecifikuje, podle jakého pravidla se vrchol~$v$ vybírá. Pøesto +ale mù¾eme dokázat nìkolik zajímavých tvrzení, která na~konkrétním zpùsobu +výbìru nezávisejí. + +\>\s{Vìta:} Spustíme-li meta-algoritmus na graf bez záporných cyklù, pak: + +\numlist\nparen +\:Ohodnocení $h(v)$ v¾dy odpovídá délce nìjakého sledu -- doká¾eme indukcí podle poètu krokù algoritmu. +\:$h(v)$ dokonce odpovídá délce nìjaké cesty -- staèí rozmyslet, v~jaké situaci by vytvoøený sled mohl obsahovat cyklus. +\:Algoritmus se v¾dy zastaví -- cest, a~tím pádem i mo¾ných hodnot~$h(v)$ pro + ka¾dý~$v$, je koneènì mnoho. +\:Po zastavení jsou oznaèeny jako uzavøené právì ty vrcholy, které jsou dosa¾itelné z~$u$ -- + implikace $\Rightarrow$ je triviální, pro $\Leftarrow$ staèí uvá¾it neuzavøený vrchol, + který je dosa¾itelný z~$u$ cestou o~co nejmen¹ím poètu hran. +\:Po zastavení mají koneèné~$h(v)$ právì v¹echny uzavøené vrcholy. +\:Pro ka¾dý dosa¾itelný vrchol je na~konci $h(v)$ rovno $d(u,v)$ -- kdyby to nebyla pravda, + vyberme si ze~\uv{¹patných} vrcholù~$v$ takový, pro nìj¾ obsahuje nejkrat¹í cesta z~$u$ do~$v$ + nejmen¹í mo¾ný poèet hran. Vrchol~$v$ je zajisté rùzný od~$u$, tak¾e má na~této cestì nìjakého + pøedchùdce~$w$. Pøitom~$w$ u¾ musí být ohodnocen správnì a relaxace, která mu toto ohodnocení + nastavila, ho musela prohlásit za~otevøený. Jen¾e ka¾dý otevøený vrchol je pozdìji uzavøen, + tak¾e~$w$ poté musel být je¹tì alespoò jednou relaxován, co¾ muselo sní¾it~$h(v)$ na správnou + vzdálenost. +\endlist + +\>Meta-algoritmus tedy pro libovolnou implementaci kroku~4 spoèítá správné vzdálenosti. +\qed + +\>\s{Cvièení:} +\itemize\ibull +\:Nech» do algoritmu doplníme udr¾ování pøedchùdcù tak, ¾e v~kroku~9 + pøenastavíme pøedchùdce vrcholu~$w$ na vrchol~$u$. Doka¾te, ¾e pøedchùdci + dosa¾itelných vrcholù budou tvoøit strom a ¾e tento strom bude stromem + nejkrat¹ích cest z~vrcholu~$u$. +\:Doka¾te, ¾e pro graf, v~nìm¾ je alespoò jeden záporný cyklus dosa¾itelný + z~poèáteèního vrcholu, se algoritmus nezastaví a ohodnocení v¹ech vrcholù + na cyklu postupnì klesnou libovolnì hluboko. Nedosa¾itelné záporné cykly + chod algoritmu samozøejmì nijak neovlivní. +\endlist + +\h{Bellmanùv-Fordùv-Mooreùv algoritmus} + +Bellman, Ford a Moore objevili nezávisle na sobì algoritmus (øíkejme mu BFM), +který lze v~øeèi na¹eho meta-algoritmu formulovat takto: Otevøené vrcholy +udr¾ujeme ve~frontì (v¾dy relaxujeme vrchol na poèátku fronty, novì otevírané +zaøazujeme na~konec. Co toto pravidlo zpùsobí? + +\>\s{Vìta:} Èasová slo¾itost algoritmu~BFM je $\O(nm)$. + +\proof +Bìh algoritmu rozdìlíme na~fáze. Nultá fáze sestává z~vlo¾ení vrcholu~$u$ +do~fronty. V~$(i+1)$-ní fázi relaxujeme ty vrcholy, které byly do~fronty +ulo¾eny bìhem $i$-té fáze. + +V¹imneme si, ¾e na~konci $i$-té fáze je ka¾dé ohodnocení $h(v)$ shora omezeno +délkou nejkrat¹ího ze~sledù z~$u$ do~$v$, které mají nejvý¹e~$i$ hran. Fází je +tedy nejvý¹e~$n$. + +Relaxace ka¾dého vrcholu pøitom trvá lineárnì se stupnìm vrcholu, tak¾e celá +fáze probìhne v~èase $\O(m)$. +\qed + +\>\s{Cvièení:} +\itemize\ibull +\:Uka¾te, ¾e asymptoticky stejné èasové slo¾itosti by dosáhl algoritmus, + který by vrcholy oèísloval $v_1,\ldots,v_n$ a opakovanì by je v~tomto + poøadí relaxoval tak dlouho, dokud by se ohodnocení mìnila. +\:Jak algoritmus upravit, aby v~$i$-té fázi spoèítal minimální délky sledù + o~právì~$i$ hranách? +\:Jak lze algoritmus BFM vyu¾ít k~nalezení záporného cyklu? +\endlist + +\h{Dijkstrùv algoritmus} + +Pokud jsou v¹echny délky hran nezáporné, mù¾eme pou¾ít efektivnìj¹í pravidlo +pro výbìr vrcholu navr¾ené Dijkstrou. To øíká, ¾e v¾dy relaxujeme ten z~otevøených +vrcholù, jeho¾ ohodnocení je nejmen¹í. + +\>\s{Vìta:} Dijkstrùv algoritmus uzavírá vrcholy v~poøadí podle neklesající +vzdálenosti od~$u$ a ka¾dý dosa¾itelný vrchol uzavøe právì jednou. + +\proof +Indukcí doká¾eme, ¾e v~ka¾dém okam¾iku mají v¹echny uzavøené vrcholy ohodnocení +men¹í nebo rovné ohodnocením v¹ech otevøených vrcholù. Na~poèátku to jistì platí. +Nech» nyní uzavíráme vrchol~$v$ s~minimálním $h(v)$ mezi otevøenými. Bìhem jeho +relaxace nemù¾eme ¾ádnou hodnotu sní¾it pod~$h(v)$, jeliko¾ v~grafu s~nezápornými +hranami je $h(v) + \ell(v,w) \ge h(v)$. Hodnota zbývajících otevøených vrcholù +tedy neklesne pod hodnotu tohoto novì uzavøeného. Hodnoty døíve uzavøených vrcholù +se nemohou nijak zmìnit. +\qed + +Pøímoèará implementace Dijkstrova algoritmu by tedy poka¾dé v~èase $\O(n)$ +vybrala otevøený vrchol s~nejmen¹ím ohodnocením, v~èase $\O(n)$ ho relaxovala +a toto by se opakovalo nejvý¹e $n$-krát. Algoritmus by tedy dobìhl v~èase $\O(n^2)$, +co¾ je pro husté grafy zajisté optimální. Zkusíme tedy algoritmus zrychlit +na~øídkých grafech. + +V¹echny relaxace trvají dohromady $\O(\sum_v \deg(v)) = \O(m)$, tak¾e úzkým hrdlem je +vybírání minima. Pou¾ijeme tedy vhodnou datovou strukturu, v~ní¾ budeme udr¾ovat +mno¾inu v¹ech otevøených vrcholù spolu s~jejich ohodnoceními. Od~datové struktury +potøebujeme, aby umìla operace \ (vlo¾ení vrcholu), \ (nalezení +a smazání minima) a \ (sní¾ení hodnoty vrcholu). První dvì operace pøitom +voláme nejvý¹e $n$-krát a operaci \ nejvý¹e $m$-krát. Celý algoritmus +tedy dobìhne v~èase +$$\O(nT_I(n) + nT_E(n) + mT_D(n)),$$ +kde $T_I(n)$, $T_E(n)$ a $T_D(n)$ jsou èasové slo¾itosti jednotlivých operací +na~struktuøe o~nejvý¹e~$n$ prvcích (staèí amortizovanì). + +\>Jaké mo¾nosti máme pro volbu struktury? + +\itemize\ibull +\:{\I pole} -- \ a \ stojí konstantu, \ trvá $\O(n)$, + celkem tedy $\O(n^2)$. +\:{\I (binární) halda} -- v¹echny tøi operace umíme provést v~èase $\O(\log n)$, tak¾e celkem + $\O(m\log n)$. To je pro husté grafy hor¹í, pro øídké lep¹í. +\:{\I $k$-regulární halda} -- pokud haldu upravíme tak, ¾e ka¾dý vrchol bude mít a¾ $k$ synù, + hloubka haldy klesne na~$\O(\log_k n)$. Operace \uv{vybublávající} prvky smìrem nahoru, + co¾ je \ a \, se zrychlí na~$\O(\log_k n)$. Ov¹em \ potøebuje + zkoumat v¹echny syny ka¾dého nav¹tíveného prvku, tak¾e se zpomalí na $\O(k\log_k n)$. + + Celková slo¾itost tedy vyjde $\O(nk\log_k n + m\log_k n)$. Oba èleny se vyrovnají + pro $k=m/n$, èím¾ získáme $\O(m\log_{m/n} n)$. Tento logaritmus je pøitom $\O(1)$, + kdykoliv je $m\ge n^{1+\varepsilon}$ pro nìjaké~$\varepsilon>0$, tak¾e pro dostateènì + husté grafy je algoritmus lineární. + + (V¹imnìte si, ¾e pro $m\approx n^2$ algoritmus zvolí $k\approx n$, tak¾e halda degeneruje + na jediné patro, tedy na pole, které se opravdu ukázalo jako optimální volba pro husté grafy.) +\:{\I Fibonacciho halda} -- \ a \ stojí $\O(1)$, \ má slo¾itost + $\O(\log n)$ [v¹e amortizovanì]. Dijkstrùv algoritmus tedy dobìhne v~èase $\O(m + n\log n)$. + To je lineární pro grafy s~hustotou $\Omega(\log n)$. +\:{\I Datové struktury pro èísla omezeného rozsahu} -- prozkoumáme vzápìtí. +\endlist + +\>\s{Cvièení:} + +\itemize\ibull +\:Najdìte pøíklad nìjakého grafu se zápornými hranami (ale bez záporných cyklù), + na~kterém Dijkstrùv algoritmus sel¾e. +\:Rozmyslete si, ¾e pokud nevyu¾ijeme nìjaké speciální vlastnosti èísel (celoèíselnost, + omezený rozsah), je mez $\O(m+n\log n)$ nejlep¹í mo¾ná, proto¾e Dijkstrovým algoritmem + mù¾eme tøídit $n$-tici èísel. +\:Jsou-li délky hran celoèíselné, mù¾eme se na Dijkstrùv algoritmus dívat i takto: + Pøedstavme si, ¾e ka¾dou hranu nahradíme cestou tvoøenou hranami jednotkové délky + a na vzniklý neohodnocený graf spustíme prohledávání do~¹íøky. To samozøejmì vydá + správný výsledek, ale pomìrnì pomalu, proto¾e bude vìt¹inu èasu trávit posouváním + vlny \uv{uvnitø} pùvodních hran. Mù¾eme si tedy pro ka¾dou pùvodní hranu naøídit + \uv{budík}, který nám øekne, za~kolik posunutí vlny dospìjeme na~její konec. + Doka¾te, ¾e tento algoritmus je ekvivalentní s~Dijkstrovým. +\endlist + +%\references +\bye diff --git a/13-dijkstra/Makefile b/13-dijkstra/Makefile new file mode 100644 index 0000000..70b033a --- /dev/null +++ b/13-dijkstra/Makefile @@ -0,0 +1,3 @@ +P=13-dijkstra + +include ../Makerules