\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>		& \<Pomocná pamì»>	& \<Stabilní> \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:

\s{Vìta:}
Ka¾dý deterministický tøídící algoritmus zalo¾ený na porovnávání a kopírování prvkù
potøebuje na vstup délky $n$ v nejhor¹ím pøípadì $\Omega (n \log n)$ porovnání.

O pravdìpodobnostních se v prùmìru dá dokázat podobná vìta.

\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 kopíruje setøídìné prvky do
výsledného pole.\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\}$. Toto je také bez újmy na
obecnosti, proto¾e dokazujeme ¾e existuje vstup na kterém pobì¾í aspoò takhle dlouho.

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
urèil permutaci. Porovnáváme prvky na jejich pùvodních pozicích. Nikdo nám nebrání
porovnat prvek sám se sebou (proto mù¾e nastat rovnost) nebo porovnat nìco
nìkolikrát\dots

Hloubka rozhodovacího stromu odpovídá poètu porovnání. My chceme dokázat, ¾e porovnání
musí být aspoò $\Omega (n\log n)$.

\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ù. Kompletní prùbìh algoritmu je urèen pouze výsledky
porovnání. 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:} Ternární strom hloubky $k$ má nejvý¹e $3^k$ listù.
  \par\noindent {\sl Dùkazík:} Uva¾me terná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 $3^i$
  vrcholù, v¹ech listù je nejvý¹e $3^k$.
  \qed
}

\>Z~tohoto lemmátka plyne, ¾e rozhodovací strom musí být hluboký alespoò $\log_3 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:
\::$ \<pridej>(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:
\:::$ \<vezmi>(P_i, r) $
\:::$ \<pridej>(Q_{r[i]}, r) $
\::Pro $ j \leftarrow 1 $ do velikost($S$) opakuj:
\:::$ \<vezmi>(S, r) $
\:::$ \<pridej>(Q_{r[i]}, r) $

\::$ S \leftarrow \emptyset $
\::Pro $ j \leftarrow 1 $ do $ R $ opakuj:
\:::Pro $ k \leftarrow 1 $ do velikost($Q_j$) opakuj:
\::::$ \<vezmi>(Q_j, r) $
\::::$ \<pridej>(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>		& \<Pomocná pamì»>	& \<Stabilní> \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
