diff --git a/KIV PPA2/Prednaska03.md b/KIV PPA2/Prednaska03.md index 25b535c..804e45e 100644 --- a/KIV PPA2/Prednaska03.md +++ b/KIV PPA2/Prednaska03.md @@ -14,32 +14,32 @@ ## O-notace -$O(f(n))$ je množina všech funkcí, pro které platí, že $g(n) < c \cdot f(n)$ pro všechna $n > n_0 > 0$ a nějaké $c > 0$. +$\mathcal{O}(f(n))$ je množina všech funkcí, pro které platí, že $g(n) < c \cdot f(n)$ pro všechna $n > n_0 > 0$ a nějaké $c > 0$. -- je-li $g(n) \in O(f(n))$, pak +- je-li $g(n) \in \mathcal{O}(f(n))$, pak - $g(n)$ se vejde pod $f(n)$ - $g(n)$ neroste rychleji než $f(n)$ **Význam O-notace:** - omezuje funkci jen shora - je záruka (**nebude to horší než...**) -- příslušnost do $O(f(n))$ implikuje **efektivitu**, pokud $f(n)$ roste dost pomalu +- příslušnost do $\mathcal{O}(f(n))$ implikuje **efektivitu**, pokud $f(n)$ roste dost pomalu U funkcí nezáleží na přenásobení konstantou, přičtení konstanty ani na základu logaritmu: -| pokud | platí také | -| ---------------------- | ------------------ | -| $g(n) \in O(kf(n))$ | $g(n) \in O(f(n))$ | -| $g(n) \in O(k + f(n))$ | $g(n) \in O(f(n))$ | -| $O(\log_{a}(n)) =$ | $O(\log_{b}(n))$ | +| pokud | platí také | +| ----------------------------------- | ----------------------------------- | +| $g(n) \in \mathcal{O}(kf(n))$ | $g(n) \in \mathcal{O}(f(n))$ | +| $g(n) \in \mathcal{O}(k + f(n))$ | $g(n) \in \mathcal{O}(f(n))$ | +| $g(n) \in \mathcal{O}(\log_{a}(n))$ | $g(n) \in \mathcal{O}(\log_{b}(n))$ | -Mezi některými množinami $O(f(n))$ platí tyto vztahy: +Mezi některými množinami $\mathcal{O}(f(n))$ platí tyto vztahy: $$ -O(1) \subset O(\log(n)) \subset O(\sqrt{n}) \subset 0(n) \subset O(n \cdot \log(n)) \subset O(n^2) \subset O(n^3) \subset O(2^n) \subset 0(e^n) +\mathcal{O}(1) \subset \mathcal{O}(\log(n)) \subset \mathcal{O}(\sqrt{n}) \subset \mathcal{O}(n) \subset \mathcal{O}(n \cdot \log(n)) \subset \mathcal{O}(n^2) \subset \mathcal{O}(n^3) \subset \mathcal{O}(2^n) \subset \mathcal{O}(e^n) $$ -U 0-notace nezáleží na násobku ani na přičtení konstanty či menší funkce, proto se např. $g(n) = 2 \cdot n^2 + 5 \cdot n + 4$ vejde pod $0(n^2)$. +U O-notace nezáleží na násobku ani na přičtení konstanty či menší funkce, proto se např. $g(n) = 2 \cdot n^2 + 5 \cdot n + 4$ vejde pod $\mathcal{O}(n^2)$. ![Zanořování O množin](_assets/zanorovani-o-mnozin.png) @@ -59,7 +59,7 @@ Mezi Omega množinami existují **obrácené vztahy** oproti O-množinám. ## Theta notace -Pokud $g(n) \in O(f(n))$ a zároveň $g(n) \in \Omega(f(n))$, pak $g(n) \in \Theta(f(n))$. +Pokud $g(n) \in \mathcal{O}(f(n))$ a zároveň $g(n) \in \Omega(f(n))$, pak $g(n) \in \Theta(f(n))$. - neroste rychleji ani pomaleji - roste stejně rychle - graf $g(n)$ je od jistého $n_{0}$ možné uzavřít mezi grafy $c_{1} \cdot f(n)$ a $c_{2} \cdot f(n)$ - patrně $c_{1} < c_{2}$ diff --git a/KIV PPA2/Prednaska04.md b/KIV PPA2/Prednaska04.md new file mode 100644 index 0000000..a9868ce --- /dev/null +++ b/KIV PPA2/Prednaska04.md @@ -0,0 +1,119 @@ +# Řazení + +Nalezení pořadí (indexů) pro množinu prvků podle nějakého uspořádání. (neplést s tříděním) +- jeden z nejčastějších výpočetních úkonů +- součást mnoha složitějších algoritmů +- až 30 % častu běžného počítače +- rychlost algoritmů se dá dobře popsat pomocí jejich výpočetní složitosti + +**Dělení** +- **vnitřní**: jen paměťová místa, kde jsou uložená data a konstantní počet dodatečných +- **vnější**: používá $\Omega(n)$ dodatečných paměťových míst ++ **přímé**: přesouvá (řadí) samotná data (jednoduché datové typy) ++ **nepřímé**: přesouvá jen zástupce dat (složitější datové typy) +- **nestabilní**: v případě rovnosti mohou pořadí stejných prvků změnit +- **stabilní**: v případě rovnosti zachovává původní pořadí prvků + - u stabilního řazení můžeme řadit vícekrát a předchozí pořadí bude zachováno ++ **porovnávací**: většina algoritmů, porovnává vždy dvojici prvků ++ **jiné**: jen speciální případy, např. počítací řazení + +**Počítací řazení** +- omezený počet klíčů +- pouze pro přímé řazení +- používá se k určení, **kolikrát** se vyskytuje každý klíč + +## Uspořádání + +- relace $\leq$ na množině všech možných prvků +- vlastnosti: + - **tranzitivní**: když A $\leq$ B a $B \leq C$, pak $A \leq C$ + - **antisymetrická**: $A \leq B$ a $B \leq A$ jedině, když $A = B$ + - **trichotomická**: buď $A \leq B$, nebo $B \leq A$ + +### Uspořádání v Javě + +- reprezentováno implementací rozhraní **Comparable** + - A je v relaci s B právě když A.compareTo(B) $\leq 0$ + - implementace **compareTo** musí také splňovat vlastnosti uspořádání + +## Druhy řazení + +**Insert sort** (řazení vkládáním) +- uvažuje vždy jeden prvek +- postupně ho posouvá na správné místo v již seřazené části posloupnosti +- složitost: + - záleží na charakteru dat + - v nejhorším případě $\Omega(n^2)$ + - v průměrném případě také $\Omega(n^2)$ + - náročné jsou prvky vzdálené od správné pozice + - pokud žádný prvek od své správné pozice není dále než k, kde k nezávisí na n, je složitost $\mathcal{O}(n)$ + +**Shell sort** (Shellovo řazení) +- data budou zpracovávání v několika průchodech +- v počátečních průchodech kontrolovány a přesouvány prvky vzdálené o krok $h > 1$ +- délka kroku $h$ se bude postupně snižovat +- v posledním průchodu $h = 1$ a proces je stejný jako u insert sortu, nicméně většina prvků se bude posouvat jen o malou vzdálenost +- složitost: + - dosud se ji nepodařilo přesně zjistit, dle experimentů $\mathcal{O}(n^{1.25})$ + - není známa ani optimální posloupnost kroků +- nestabilní + +**QuickSort** (řazení dělením) +- rozdělí problém na menší podproblémy a ty dále dělí stejným způsobem +- končí, když je podproblém trviální +- přirozeně vede na rekurzivní zápis +- postup: + - vybere se pivot (např. poslední prvek) a přesune se do proměnné + - pole se prochází zleva a při nalezení něčeho většího než je pivot se prvek přesune na volené místo (tedy to místo posledního prvku) + - poté se začne procházet od konce, a postup se opakuje, dokud se indexy zleva i zprava nerovnají +- vlastnosti: + - není potřeba vyměňovat prvky (je to drahá operace) + - využijeme skutečné parametry left a right jako proměnné +- problém: nevhodný pivot + - ideálním pivotem je medián +- složitost: + - nejhorší případ: $\Omega(n^2)$ + - seřazená posloupnost + - nejlepší případ: $\mathcal{O}(n)$ + - medián vybrán jako první pivot + - očekávaný případ: $\approx 2n \in \mathcal{O}(n)$ + - průměrný případ: $\mathcal{O}(n \log(n))$ + +**QuickSelect** +- podobný řazení dělením +- najde prvek v k-tém pořadí velikosti (ten který by po seřazení byl na k-tém indexu) +- postup: + - vybere se pivot + - interval se rozdělí podle pivotu stejně jako při řazení + - vybere se podinterval, ve kterém leží cílový index +- efektivní pro hledání mediánu či kvantilů + +**Mergesort** (řazení slučováním) +- myšlenka: sloučení dvou posloupností je možné v lineárním čase +- vnější řazení (bude potřeba paměť navíc) +- postup: + - procházíme obě posloupnosti současně (držíme aktuální index pro obě) + - do výsledné posloupnosti zapíšeme menší ze dvou aktuálních prvků + - v příslušné posloupnosti se posuneme na další prvek +- efentivní implementace: + - pracuje s úseky původního pole a eliminuje testování konce polí A a B + - alokuje se dočasné pole + - v poli se vytvoří tzv. bitonická posloupnost + - prvky A v původním pořadí, prvky v B v obráceném pořadí + - z pomocného pole se prvky sloučí do původního pole +- složitost: + - $\mathcal{O}(n \log(n))$ ve všech případech +- paměťová složitost: + - $\Omega(n)$ nutná pro uložení bitonické posloupnosti +- ve většině implementací stabilní +- snadný přepis na nerekurzivní verzi + +### Porovnání složitosti algoritmů + +| algoritmus | nejhorší | očekávaná | paměťová | +| ---------- | ------------------- | ------------------- | ----------- | +| BubbleSort | $\Theta(n^2)$ | $\Theta(n^2)$ | $\Theta(1)$ | +| InsertSort | $\Theta(n^2)$ | $\Theta(n^2)$ | $\Theta(1)$ | +| ShellSort | $\Theta(n^{3/2})$ | $\Theta(n^{1.25})$? | $\Theta(1)$ | +| QuickSort | $\Theta(n^2)$ | $\Theta(n \log(n))$ | $\Theta(1)$ | +| MergeSort | $\Theta(n \log(n))$ | $\Theta(n \log(n))$ | $\Theta(n)$ | \ No newline at end of file