From d98289cc52361f3f8881b120272943cfb1b4b9db Mon Sep 17 00:00:00 2001 From: Martin Mares Date: Mon, 30 May 2011 13:38:38 +0200 Subject: [PATCH] Trideni a Strassen: Zapisky od Karla --- 13-trideni/13-trideni.tex | 445 ++++++++++++++++++ 13-trideni/Makefile | 3 + .../5-qs => 13-trideni}/RozhodovaciStrom.eps | Bin .../nasobeni-matic-2.eps | Bin .../nasobeni-matic.eps | Bin 5 files changed, 448 insertions(+) create mode 100644 13-trideni/13-trideni.tex create mode 100644 13-trideni/Makefile rename {2007/5-qs => 13-trideni}/RozhodovaciStrom.eps (100%) rename {2007/3-strassen => 13-trideni}/nasobeni-matic-2.eps (100%) rename {2007/3-strassen => 13-trideni}/nasobeni-matic.eps (100%) diff --git a/13-trideni/13-trideni.tex b/13-trideni/13-trideni.tex new file mode 100644 index 0000000..46914df --- /dev/null +++ b/13-trideni/13-trideni.tex @@ -0,0 +1,445 @@ +\input ../lecnotes.tex + +\prednaska{13}{Tøídící algoritmy a násobení matic}{} + +Minulou pøedná¹ku jsme probírali QuickSort, jeden z historicky prvních tøídících algoritmù, +které pøekonaly kvadratickou slo¾itost aspoò v~prùmìrném pøípadì. + +Proè se dodnes pou¾ívá, kdy¾ známe algoritmy, které mají slo¾itost $\Theta(n\log n)$ i +v nejhor¹ím pøípadì? Proto¾e QuickSort se k pamìti chová témìø sekvenènì, tak¾e na +dne¹ních poèítaèích bì¾í rychle. + +Podívejme se na pár nápadù pro skuteèné naprogramování tohoto algoritmu. + +Urèitì nám vadí pøekopírovávat z a do pomocných polí. Na¹tìstí mù¾eme pøerovnávat +pøímo v pùvodním poli. Zleva budeme dávat prvky men¹í ne¾ pivot, zprava vìt¹í ne¾ +pivot. Na toto staèí udr¾ovat si dva indexy $a$ a $b$, které znaèí, jak daleko vlevo +(vpravo) u¾ jsou správné prvky. + +Rekurzivní programy mohou být pro poèítaè zátì¾í. Proto implementujme vlastní zásobník +na hranice úsekù, které zbývá setøídit. V¾dy vìt¹í interval vlo¾íme na zásobník a men¹í +rovnou zpracujeme. Na zásobníku tedy bude maximálnì $\log n$ polo¾ek. + +Malé podproblémy dotøídíme nìjakým triviálním algoritmem napøíklad InsertSortem. +Odhadem pro $n=10$ je to pro dne¹ní poèítaèe výhodné. + +\h{Zoo tøídících algoritmù} + +Porovnejme nyní známé tøídící algoritmy. + +Bude nás zajímat i jejich stabilita, tedy jestli zachovají poøadí polo¾ek se stejným +klíèem. Stabilita je dùle¾itá pøi lexikografickém tøídìní, kde se napøed tøídí podle +nejménì významné slo¾ky a pak podle významìj¹ích. + +\s{Definice:} {\it Stabilní tøídìní} øíkáme takovému, které u~prvkù se stejnou hodnotou +klíèe zachová jejich vzájemné poøadí, v~jakém byly na~vstupu. + +\medskip + +\settabs 4 \columns +\+ & \<Èas> & \ & \ \cr +\smallskip +\+ InsertSort & $\Theta (n^2)$ & $\Theta (1)$ & $+$ \cr +\+ MergeSort & $\Theta (n\log n)$ & $\Theta (n)$ & $+$ \cr +\+ HeapSort & $\Theta (n\log n)$ & $\Theta (1)$ & $-$ \cr +\+ QuickSort & $\Theta (n\log n)$ & $\Theta (\log n)$ & $-$ \cr + +\medskip + +\>Poznámky k tabulce: +\itemize\ibull +\:QuickSort má jen prùmìrnou èasovou slo¾itost $\Theta (\log n)$. Mù¾eme ale øíct, ¾e +porovnáváme prùmìrné èasové slo¾itosti, proto¾e u ostatních algoritmù vyjdou stejnì +jako jejich èasové slo¾itosti v~nejhor¹ím pøípadì. +\:HeapSort -- tøídìní pomocí haldy. Do haldy vlo¾íme v¹echny prvky a pak je vybereme. +Celkem $n$ operací s haldou, ka¾dá za $\log n$. Navíc tuto haldu mohu stavìt i boøit +v poli, ve kterém dostanu vstup. +\:MergeSort jde implementovat s konstantní pamì»ovou slo¾itostí. +\:MergeSort je stabilní, kdy¾ dìlím pole na poloviny. Není pøi tøídìní spojových seznamù +s~rozdìlováním prvkù na sudé a liché. +\:QuickSort se dá naprogramovat stabilnì, ale potøebuje lineárnì pomocné pamìti. +\endlist + +\>®ádný algoritmus v tabulce netøídil rychleji ne¾ $\Theta(n\log n)$. To není náhoda +-- nasledující vìta nám øíká, ¾e to nejde: + +{\bf XXX: Letos jsem vìtu dokazoval trochu jinak, nutno pøedìlat.} + +\s{Vìta:} +Ka¾dý tøídící algoritmus zalo¾ený na porovnávání a prohazování prvkù +potøebuje na vstup délky $n$ v nejhor¹ím pøípadì $\Omega (n \log n)$ porovnání. + +\proof +Bez újmy na obecnosti budeme nejdøíve pøedpokládat o algoritmu dvì vìci: +Jednak to, ¾e algoritmus nejprve porovnává, a teprve potom prohazuje.\foot{Algoritmus +mù¾eme upravit tak, aby si pamatoval aktuální permutaci prvkù a podle ní prohazoval a¾ na konci.} +Také pøedpokládáme, ¾e vstup algoritmu je permutace na mno¾inì $\{1, \ldots, n\}$. + +Chování tohoto algoritmu popí¹eme rozhodovacím stromem. V rozhodovacím stromu vnitøní vrcholy +urèují jednotlivá porovnání prvkù a listy odpovídají okam¾ikùm, kdy algoritmus pøestal porovnávat a zaèal prohazovat. + +\figure{RozhodovaciStrom.eps}{Rozhodovací Strom}{0.3\hsize} + +Vstup je permutace $n$ prvkù, a víme ¾e poèet rùzných permutací je $n!$. Existuje tedy právì $n!$ rùzných vstupù. +Dále si v¹imneme, ¾e nemohou existovat dvì vstupní permutace, pro které by algoritmus skonèil v tém¾e listu rozhodovacího stromu. +Listù stromu je tedy alespoò tolik, kolik je rùzných vstupù, tedy $n!$. + +{\narrower + \s{Lemmátko:} Binární strom hloubky $k$ má nejvý¹e $2^k$ listù. + \par\noindent {\sl Dùkazík:} Uva¾me binární strom hloubky $k$ s maximálním poètem + listù. V takovém stromu budou v¹echny listy urèitì le¾et na poslední hladinì + (kdyby nele¾ely, mù¾eme pod nìkterý list na vy¹¹í hladinì pøidat dal¹í dva vrcholy a získat + tak \uv{listnatìj¹í} strom stejné hloubky). Jeliko¾ na $i$-té hladinì je nejvý¹e $2^i$ + vrcholù, v¹ech listù je nejvý¹e $2^k$. + \qed +} + +\>Z~tohoto lemmátka plyne, ¾e rozhodovací strom musí být hluboký alespoò $\log n!$. + +\>Zbytek je u¾ snadné cvièení z diskrétní matematiky: + +{\narrower + \s{Lemmátko:} $ n! \ge n^{n / 2}$. + \par\noindent {\sl Dùkazík:} $n! = \sqrt{(n!)^2} = \sqrt{1(n-1)\cdot 2(n-2) \cdot \ldots \cdot n\cdot 1}$, + co¾ mù¾eme také zapsat jako $\sqrt{1(n-1)}\cdot \sqrt{2(n-2)} \cdot \ldots \cdot \sqrt{n\cdot 1}$. + Pøitom pro ka¾dé $1\le k\le n$ je $k(n+1-k) = kn + k - k^2 = n + (k-1)n + k(1-k) = n + (k-1)(n-k) \ge n$. + Proto je ka¾dá z~odmocnin vìt¹í nebo rovna $n^{1/2}$ a $n!\ge (n^{1/2})^n = n^{n/2}$. + \qed +} + +\>Hloubka stromu tedy èiní minimálnì $\log n! \ge \log(n^{n/2}) = n/2 \cdot \log n = \Omega(n\log n)$, +co¾ také zdola odhaduje poèet porovnání, který algoritmus provede v nejhor¹ím pøípadì. +\qed + +Ukázali jsme si tøídìní v èase $ \O(N\log{N}) $ a také dokázali, ¾e líp to v obecném +pøípadì nejde. Na¹e tøídící algoritmy jsou tedy optimální (a¾ na multiplikativní konstantu). +Opravdu? + +Pøekvapivì mù¾eme tøídit i rychleji -- vìta omezuje pouze tøídìní pomocí porovnávaní. +Co kdy¾ o~vstupu víme víc, tøeba ¾e je tvoøen èísly z~omezeného rozsahu. + +\h{Counting sort} + +Counting sort je algoritmus pro tøídìní $N$ celých èísel s maximálním rozsahem hodnot +$R$. Tøídí v èase $ \O(N + R) $ s~pamì»ovou nároèností $ \O(R) $. + +Algoritmus postupnì prochází vstup a poèítá si ke ka¾dému prvku z~rozsahu kolikrát jej +vidìl. Poté a¾ projde celý vstup, projde poèítadla a indexy u~tech, kde nìco napoèítal +vydá jako výstup. + +\s{Algoritmus:} (tøídìní posloupnosti $\left ( x_i \right )_0^N$ pomocí {\it Counting sort}u) + +\algo +\:Pro $ i \leftarrow 1 $ do $R$ opakuj: +\::$ p_i \leftarrow 0 $ +\:Pro $ i \leftarrow 1 $ do $N$ opakuj: +\::$ p_{x_i} \leftarrow p_{x_i} + 1 $ +\:$ j \leftarrow 1 $ +\:Pro $ i \leftarrow 1 $ do $R$ opakuj: +\::Pokud $ p_i \neq 0 $ +\:::$ v_j \leftarrow i $ +\:::$ j \leftarrow j + 1 $ +\:Vra» $v$ +\endalgo + +\h{Pøihrádkové tøídìní} + +Counting sort nám moc nepomù¾e, pokud chceme tøídit neco jiného ne¾ celá èísla, ale i +to se dá øe¹it. Pøihrádkové, popøípadì kbelíkové tøídìní, neboli bucket-sort umí +tøídit mno¾inu prvkù oèíslovaných èísly $ 1 \ldots R $. + +\>Potøebuje k tomu èas $ \O(N + R) $ a pamì» $ \O(N + R) $. + +Bucket sort ma je¹tì jednu pøíjemnou vlastnost: jedná se o stabilní tøídìní. + +\s{Algoritmus:} (tøídìní mno¾iny $x_1, x_2, \ldots, x_n $ oèíslované $c_1, c_2, \ldots, c_n$ pomocí {\it bucket-sort}u) +\algo +\:$P_1 \dots P_R \leftarrow \emptyset$ +\:Pro $i=1\dots n$: +\::vlo¾ $x_i$ do $P_{x_i}$ +\:Pro $j=1\dots R$ +\::vypi¹ co je v $P_j$ +\endalgo + +\h{Lexikografické tøídìní k-tic} +Mìjme $n$ uspoøádaných $k$-tic prvkù z mno¾iny $ \{1 \ldots R \}^k $. Úkol zní seøadit +\hbox{$k$-tice} slovníkovì (lexikograficky). Mù¾eme pou¾ít metodu rozdìl a panuj, +tak¾e prvky setøídíme nejprve podle první souøadnice $k$-tic a pak se rekurzivnì +zavoláme na ka¾dou pøihrádku a tøídíme podle následující souøadnice. +% a dal?? +Nebo mù¾eme vyu¾ít toho, ¾e bucket-sort je stabilní a tøídit takto: + +\s{Algoritmus: } (tøídìní $k$-tic $ x_1, x_2, \ldots ,x_n $ lexikograficky pomocí {\it Bucket-sort}u) +\algo +\:$ S \leftarrow \{x_1, x_2, \dots, x_n\} $ +\:Pro $ i \leftarrow k $ do $ 1 $ opakuj: +\::$ S \leftarrow $ bucket-sort $ S $ podle $i$-té souøadnice +\:vysledek $ S $ +\endalgo + +Pro pøehlednost v následujícím pozorování oznaème $ l = k - i + 1 $, co¾ pøesnì odpovídá tomu, v kolikátém prùchodu cyklu jsme. + +\s{Pozorování:} +Po $l$-tém prùchodu cyklem jsou prvky uspoøádány lexikograficky podle $i$-té a¾ $k$-té souøadnice. + +\proof +Indukcí podle $l$: +\itemize\ibull +\:Pro $ l = 1 $ jsou prvky uspoøádány podle poslední souøadnice. +\:Po $l$ prùchodech ji¾ máme prvky setøídìny lexikograficky podle $i$-té a¾ $k$-té souøadnice a spou¹tíme $(l + 1)$-ní prùchod, tj. budeme tøídít podle $(i - 1)$-ní souøadnice. +Proto¾e bucket-sort tøídí stabilnì, zùstanou prvky se stejnou $(i - 1)$-ní souøadnicí vùèi sobì seøazeny tak, jak byly seøazeny na vstupu. +Z IP tam v¹ak byly seøazeny lexikograficky podle $i$-té a¾ $k$-té souøadnice. Tudí¾ po $(l + 1)$-ním prùchodu jsou prvky seøazeny podle $(i - 1)$-ní a¾ $k$-té souøadnice. +\endlist +\qed + +Èasová slo¾itost je $\O( k \cdot (n + R))$, co¾ je lineární s délkou vstupu $(k \cdot n)$ pro pevné $k$; pamì»ová slo¾itost èiní $\O(n + R)$. + +\h{Tøídìní èísel $ 1 \ldots R $ podruhé (Radix sort)} +Zvolíme základ $Z$ a èísla zapí¹eme v soustavì o základu $Z$, èím¾ získáme $ ( \lfloor \log_z{R} \rfloor +1) $-tice, na které spustíme pøedcházející algoritmus. +Díky tomu budeme tøídít v èase $\O({\log{R} \over \log{Z}} \cdot (n + Z))$. A jak zvolit vhodnì $Z$? + +Pokud bychom zvolili $Z$ konstantní, èasová slo¾itost bude $\O(\log{R} \cdot n) $, co¾ mù¾e být $ n \log{n} $ nebo i víc. +Zvolíme-li $ Z = n $, dostáváme $ \O({\log{R} \over \log{n}} \cdot n) $, co¾ pro $ R \leq n^\alpha $ znamená $ \O(\alpha n) $. + +\h{Tøídìní øetìzcù} + +\>{\I (Na pøedná¹ce letos nebylo, ale pro úplnost uvádíme.)} + +Mìjme $n$ øetìzcù $ r_1, r_2 \dots r_n $ dlouhých $ l_1, l_2 \dots +l_n $ Oznaème si $ L = \max_{1 \leq i \leq n} l_i $ délku nejdel¹ího øetìzce a $R$ +poèet znakù abecedy. + +Problém je, ¾e øetìzce nemusí být stejnì dlouhé (pokud by byly, lze se na øetìzce +dívat jako na $k$-tice, které u¾ tøídít umíme). S tím se mù¾eme pokusit vypoøádat +doplnìním øetìzcù mezerami tak, aby mìly v¹echny stejnou délku, a spustit na nìj +algoritmus pro $k$-tice. Tím dostaneme algoritmus, který bude mít èasovou slo¾itost $ +\O(Ln) $, co¾ bohu¾el mù¾e být a¾ kvadratické vzhledem k velikosti vstupu. + +Pøíklad: na vstupu mìjme k øetìzcù, kde prvních $k-1$ z nich bude mít délku $1$ a +poslední øetìzec bude dlouhý pøesnì $k$. Vstup má tedy celkovou délku $2k-1$ a my teï +doplníme prvních $k-1$ øetìzcù mezerami. Vidíme, ¾e algoritmus teï bude pracovat v +èase $ \O(k^2) $. To, co nám nejvíce zpùsobovalo problémy u pøedchozího pøíkladu, bylo +velké mno¾ství èasu zabraného porovnáváním doplnìných mezer. Zkusíme proto øe¹it ná¹ +problem trochu chytøeji a koncové mezery do øetìzù vùbec pøidávat nebudeme. + +Nejprve roztøídíme bucket-sortem øetìzce do pøihrádek (mno¾in) $ P_i $ podle jejich +délek, kde $i$ znaèí délku øetìzcù v dané pøihrádce, neboli definujme $ P_i = \left\{ +r_j \vert l_j = i \right\} $. Dále si zavedeme seznam setøídìných øetìzcù $S$ +takový, ¾e v nìm po $k$-tém prùchodu tøídícím cyklem budou øetìzce s délkou +alespoò $ L - k + 1 $ (oznaème $l$) a zároveò v nìm tyto øetìzce budou +setøídìny lexikograficky od $l$-tého znaku po $L$-tý. Z definice tohoto +seznamu je patrné, ¾e po $L$ krocích tøídícího cyklu bude tento seznam +obsahovat v¹echny øetìzce a tyto øetìzce v nìm budou lexikograficky seøazeny. + +Zbývá u¾ jen popsat, jak tento cyklus pracuje. Nejprve vezme $l$-tou mno¾inu $ P_l $ a +její øetìzce roztøídí do pøihrádek $ Q_j $ (kde index $j$ znaèí j-tý znak abecedy) +podle jejich $l$-tého (neboli posledního) znaku. Dále vezme seznam $S$ a jeho øetìzce +pøidá opìt podle jejich $l$-tého znaku do stejných pøihrádek $ Q_j $ za ji¾ døíve +pøidané øetìzce z $ P_l $. Na závìr postupnì projde v¹echny pøihrádky $ Q_j $ a +øetìzce v nich pøesune do seznamu $S$. Proto¾e øetìzce z pøihrádek $ Q_j $ bude brát +ve stejném poøadí, v jakém do nich byly umístìny, a proto¾e ze seznamu $S$, který je +setøízený podle $ (l + 1) $-ního znaku po $L$-tý, bude také brát øetìzce postupnì, +bude seznam $S$ po $k$-tém prùchodu pøesnì takový, jaký jsme chtìli (indukcí bychom +dokázali, ¾e cyklus pracuje skuteènì správnì). Zároveò z popisu algoritmu je jasné, ¾e +bìhem tøídìní ka¾dý znak ka¾dého øetìzce pou¾ijeme právì jednou, tudí¾ algoritmus bude +lineární s délkou vstupu (pro úplnost dodejme, ¾e popsaný algoritmus funguje v +pøípadech, kdy abeceda má pevnou velikost). + +Algoritmus: (tøídìní øetìzcù) +\algo +\:$L \leftarrow \max(l_1, l_2,\ldots,l_n)$ +\:Pro $ i \leftarrow 1 $ do $L$ opakuj: +\::$ P_i \leftarrow \emptyset $ +\:Pro $ i \leftarrow 1 $ do $ n $ opakuj: +\::$ \(P_{l_i}, r_i) $ + +\:$ S \leftarrow \emptyset $ +\:Pro $ i \leftarrow L $ do $ 1 $ opakuj: +\::Pro $ j \leftarrow 1 $ do $ R $ opakuj: +\:::$ Q_j \leftarrow \emptyset $ + +\::Pro $ j \leftarrow 1 $ do velikost($P_i$) opakuj: +\:::$ \(P_i, r) $ +\:::$ \(Q_{r[i]}, r) $ +\::Pro $ j \leftarrow 1 $ do velikost($S$) opakuj: +\:::$ \(S, r) $ +\:::$ \(Q_{r[i]}, r) $ + +\::$ S \leftarrow \emptyset $ +\::Pro $ j \leftarrow 1 $ do $ R $ opakuj: +\:::Pro $ k \leftarrow 1 $ do velikost($Q_j$) opakuj: +\::::$ \(Q_j, r) $ +\::::$ \(S, r) $ + +\:výsledek $S$ +\endalgo + +Èasová slo¾itost tohoto algoritmu tedy bude $ \O(RN) $, kde $N$ je délka vstupu a $R$ poèet znakù abecedy. + +\h{Zoo tøídících algoritmù podruhé} + +\>Doplòme tedy na¹i tabulku: + +\medskip + +\settabs 4 \columns +\+ & \<Èas> & \ & \ \cr +\smallskip +\+ InsertSort & $\Theta (n^2)$ & $\Theta (1)$ & $+$ \cr +\+ MergeSort & $\Theta (n\log n)$ & $\Theta (n)$ & $+$ \cr +\+ HeapSort & $\Theta (n\log n)$ & $\Theta (1)$ & $-$ \cr +\+ QuickSort & $\Theta (n\log n)$ & $\Theta (\log n)$ & $-$ \cr +\+ BucketSort & $\Theta (n+R)$ & $\Theta (n+R)$ & $+$ \cr +\+ k-tice & $\Theta (k(n+R))$ & $\Theta (n+R)$ & $+$ \cr +\+ RadixSort & $\Theta (n\log _n R)$ & $\Theta (n)$ & $+$ \cr + +\smallskip + +\h{K èemu je vlastnì tøídìní dobré?} Díky nìmu mù¾eme rychle vyhledávat prvky v +mno¾inì, konkrétnì v èase $ \O(\log n) $ napø. pomocí pùlení intervalù. Dal¹ím +problémem, na který se hodí pou¾ít tøídìní, je zji¹tìní, zda se v posloupnosti nìjaké +její hodnoty opakují. Dá se dokázat, ¾e tuto úlohu nelze vyøe¹it lépe (rychleji), ne¾ +tak, ¾e hodnoty nejprve setøídíme a poté setøidìnou posloupnost projdeme. + +\h{Násobení matic n$\times$n a Strassenùv algoritmus} + +Nejdøíve si pøipomeneme definici násobení dvou ètvercových matic typu $n \times n$. +Platí, ¾e prvek v $i$-tém øádku a $j$-tém sloupci výsledné matice $Z$ se rovná +standardnímu skalárnímu souèinu $i$-tého øádku první matice $X$ a $j$-tého sloupce +druhé matice~$Y$. Formálnì zapsáno: + +$$ Z_{ij} = \sum_{k=1}^n X_{ik} \cdot Y_{kj}. $$ + +\figure{nasobeni-matic.eps}{Násobení matic}{\epsfxsize} + +Algoritmus, který by násobil matice podle této definice, by mìl èasovou slo¾itost $ +\Theta(n^3) $, proto¾e poèet prvkù ve výsledné matici je $n^2$ a jeden skalární souèin +vektorù dimenze $n$ vy¾aduje lineární poèet operací. + +My se s touto èasovou slo¾itostí ov¹em nespokojíme a budeme postupovat podobnì jako +pøi vylep¹ování algoritmu na násobení velkých èísel. Bez újmy na obecnosti +pøedpokládejme, ¾e budeme násobit dvì matice typu $n \times n$, kde $n=2^k, k \in \bb +N$. Obì tyto matice rozdìlíme na ètvrtiny a tyto èásti +postupnì oznaèíme u matice $X$ písmeny $A$, $B$, $C$ a $D$, u matice $Y$ písmeny $P$, +$Q$, $R$ a $S$. Z~definice násobení matic zjistíme, ¾e ètvrtiny výsledné matice $Z$ +mù¾eme zapsat pomocí souèinù èástí násobených matic. Levá horní ètvrtina bude +odpovídat výsledku operací $AP+BR$, pravá horní ètvrtina bude $AQ+BS$, levá dolní +$CP+DR$ a zbylá $CQ+DS$ (viz obrázek). + +\figure{nasobeni-matic-2.eps}{Násobení rozètvrcených matic}{\epsfxsize} + +Pøevedli jsme tedy problém násobení ètvercových matic øádu $n$ na násobení ètvercových +matic øádu ${n/2}$. Tímto rozdìlováním bychom mohli pokraèovat, dokud bychom se +nedostali na matice øádu 1, jejich¾ vynásobení je triviální. Dostali jsme tedy +klasický algoritmus typu {\it rozdìl a panuj}. Pomohli jsme si ale nìjak? V ka¾dém +kroku provádíme 8 násobení matic polovièního øádu a navíc konstantní poèet operací na +$n^2$ prvcích. Dostáváme tedy rekurentní zápis èasové slo¾itosti: + +$$ T(n) = 8T\left({n \over 2}\right) + \Theta(n^2). $$ + +Pou¾itím Master Theoremu lehce dojdeme k závìru, ¾e slo¾itost je stále $\Theta(n^3)$, tedy stejná jako pøi násobení matic z definice. Zdánlivì jsme si tedy nepomohli, ale stejnì jako tomu bylo u násobení velkých èísel, i teï mù¾eme zredukovat poèet násobení matic polovièního øádu, které nejvíce ovlivòuje èasovou slo¾itost algoritmu. Není to bohu¾el nic triviálního, a proto si radìji rovnou øekneme správné øe¹ení. Jedná se o Strassenùv algoritmus, který redukuje potøebný poèet násobení na~7, a je¹tì pøed tím, ne¾ si uká¾eme, jak funguje, doká¾eme si, jak nám to s èasovou slo¾itostí vlastnì pomù¾e: + +$$ T(n) = 7T\left({n \over 2}\right) + \Theta(n^2) \Longrightarrow \Theta(n^{\log_2 7}) = \O(n^{2.808}). $$ + +Výsledná slo¾itost Strassenova algoritmu je tedy $\O(n^{2.808})$, co¾ je sice malé, ale pro velké matice znatelné zlep¹ení oproti algoritmu vycházejícímu pøímo z~definice. + +\s{Lemma:} (vzorce pro násobení blokových matic ve~Strassenovì algoritmu) + +\def\\{\noalign{\vskip 7pt}} + +$$ +\pmatrix{A & B \cr\\ C & D \cr} +\cdot +\pmatrix{P & Q \cr\\ R & S \cr} += +\pmatrix{ +T_1 + T_4 - T_5 + T_7 & +T_3 + T_5 \cr\\ +T_2 + T_4 & +T_1 - T_2 + T_3 + T_6 \cr +},$$ + +kde: + +$$\vbox{\halign{$#$\hfil\qquad &$#$\hfil\qquad \cr +T_1 = (A+D)\cdot(P+S) & T_5 = (A+B)\cdot S \cr\\ +T_2 = (C+D)\cdot P & T_6 = (C-A)\cdot (P+Q) \cr\\ +T_3 = A\cdot(Q-S) & T_7 = (B-D)\cdot (R+S) \cr\\ +T_4 = D\cdot(R-P) \cr +}}$$ + +\medskip + +\proof Do ètvercù $4 \times 4$ si napí¹eme znaky $+$ nebo $-$ podle toho, jestli se pøi výpoètu dané matice pøièítá nebo odeèítá pøíslu¹ný souèin dvou matic. Øádky znamenají matice $A$, $B$, $C$ a $D$ a sloupce znaèí matice $P$, $Q$, $R$ a $S$. Pokud se tedy v prvním øádku a prvním sloupci vyskytuje znak $+$, znamená to ¾e pøièteme souèin matic $A$~a~$P$. Nejdøíve si spoèítáme pomocné matice $T_1$ a¾ $T_7$ a z nich pak dopoèítáme, co bude na pøíslu¹ných místech ve výsledné matici. + +\def\bbb#1{\vbox to 10pt{\vss\hbox to 10pt{\hss\tenrm #1\hss}\vss}} +\def\bb#1{\ifx#1.\bbb{$\cdot$}\else\bbb#1\fi} +\def\zz#1#2#3#4{\bb#1\bb#2\bb#3\bb#4} +\def\qq#1#2#3#4{{\offinterlineskip\vcenter{\halign{\vrule ##\vrule \cr\noalign{\hrule}\zz#1\cr\zz#2\cr\zz#3\cr\zz#4\cr\noalign{\hrule}}}}} + +$$ +T_1 = \qq{+..+}{....}{....}{+..+} \qquad +T_2 = \qq{....}{....}{+...}{+...} \qquad +T_3 = \qq{.+.-}{....}{....}{....} \qquad +T_4 = \qq{....}{....}{....}{-.+.} +$$ +$$ +T_5 = \qq{...+}{...+}{....}{....} \qquad +T_6 = \qq{--..}{....}{++..}{....} \qquad +T_7 = \qq{....}{..++}{....}{..--} +$$ + +\medskip + +\def\\{\noalign{\vskip 7pt}} +$$ +\eqalign{ +T_1 + T_4 - T_5 + T_7 &= \qq{+...}{..+.}{....}{....} = AP + BR \cr\\ +T_3 + T_5 &= \qq{.+..}{...+}{....}{....} = AQ + BS \cr\\ +T_2 + T_4 &= \qq{....}{....}{+...}{..+.} = CP + DR \cr\\ +T_1 - T_2 + T_3 + T_6 &= \qq{....}{....}{.+..}{...+} = CQ + DS \cr +} +$$ + +% konec slidu z prednasky + +Jak je vidìt, výsledná matice je tvoøena stejnými èástmi jako pøi obyèejném násobení. Touto kapitolou jsme tedy dokázali následující vìtu: + +\s{Vìta:} +Strassenùv algoritmus pro násobení matic $n \times n$ má èasovou slo¾itost +v nejhor¹ím pøípadì $\O(n^{2.808})$. \qed + +\s{Poznámka:} +Zatím nejlep¹í dokázaný algoritmus má èasovou slo¾itost $\O(n^{2.376})$, leè s velkou multiplikativní konstantou. + +\h{Dosa¾itelnost v grafech pomocí Strassenova algoritmu} + +Matice mohou souviset s mnoha na první pohled nesouvisejícími problémy. + +\s{Lemma:} Nech» $A$ je matice sousednosti grafu, nech» $S^{(k)}_{z,j}$ +oznaèíme poèet sledù délky~$k$ z vrcholu~$j$ do vrcholu $i$. Pak $S^{(k)}=A^k$. + +\proof +Indukcí podle~$k$: + +\itemize\ibull +\:$S^{(1)}=A$ +\:$S^{(k+1)}_{i,j}=\sum_{z:(i, z)\in E(G)} S^{(k)}_{z, j} = \sum _{z=1}^n A_{i, z} +S_{z, j}^{(k)}=(AS^{(k)})_{i, j}$ +\endlist +\qed + +Pøidáním smyèek do matice $A$ zjistíme dostupnost vrcholù po cestách dlouhých $k$ nebo +krat¹ích. + +Staèí tedy spoèítat matici $B=(A+E)^k$ pro libovolné $k\geq n$ ($E$ je jednotková matice). +Pak $B_{i,j}\neq 0$ právì kdy¾ existuje cesta z vrcholu $i$ do vrcholu $j$. + +Pro vypoèítání $B$ nám staèí $\lceil \log n \rceil$ umocnìní matice na druhou, co¾ je +speciální pøípad násobení matic. Èasová slo¾itost celého algoritmu tedy èiní $\O(n^{\log _2 7} \cdot \log n)$. + +Musíme v¹ak dávat pozor a normovat èísla (nulu necháme, nenulová nahradíme pøi ka¾dém +násobení jednièkou), aby nám nepøerostla pøes hlavu a hlavnì pøes maximální +integer. + +\bye diff --git a/13-trideni/Makefile b/13-trideni/Makefile new file mode 100644 index 0000000..ed75925 --- /dev/null +++ b/13-trideni/Makefile @@ -0,0 +1,3 @@ +P=13-trideni + +include ../Makerules diff --git a/2007/5-qs/RozhodovaciStrom.eps b/13-trideni/RozhodovaciStrom.eps similarity index 100% rename from 2007/5-qs/RozhodovaciStrom.eps rename to 13-trideni/RozhodovaciStrom.eps diff --git a/2007/3-strassen/nasobeni-matic-2.eps b/13-trideni/nasobeni-matic-2.eps similarity index 100% rename from 2007/3-strassen/nasobeni-matic-2.eps rename to 13-trideni/nasobeni-matic-2.eps diff --git a/2007/3-strassen/nasobeni-matic.eps b/13-trideni/nasobeni-matic.eps similarity index 100% rename from 2007/3-strassen/nasobeni-matic.eps rename to 13-trideni/nasobeni-matic.eps -- 2.39.5