Compare commits

...

3 Commits

7 changed files with 21 additions and 259 deletions

View File

@ -157,7 +157,7 @@ Vytvoříme povrch, kde hodnota je konstantní. Na vytvoření isosurface může
### Direct volume rendering
Vykreslíme objemová data přímo. Můžeme použít různé techniky, jako např. ray casting, nebo splatting.
Detailní fungování je popsané v otázce [PGV06](../PGV06_vykreslovani_objemovych_dat).
Detailní fungování je popsané v otázce [PGV06](../pgv06_vykreslovani_objemovych_dat).
## Geovizualizace

View File

@ -388,249 +388,6 @@ k + (\|S\| - k) - \|S\| = 0 & \text{pokud } \|S\| > k \\
\end{cases}
```
## Vyhledávání řetězců (string matching)
_String matching_ označuje rodinu problémů obsahující třeba:
- Nalezení prvního přesného výskytu podřetězce (_patternu_) v řetězci (_stringu_ / _textu_).
- Nalezení všech výskytů podřetězce v řetězci.
- Výpočet vzdálenosti dvou řetězců.
- Hledání opakujících se podřetězců.
Většinou je řetězec polem znaků z konečné abecedy $\Sigma$. String matching algoritmy se ale dají použít na ledacos.
Vzorek $P$ se vyskytuje v textu $T$ s posunem $s$, pokud $0 \le s \le n - m$ a zároveň $T\lbrack (s+1) .. (s + m) \rbrack = P$. Pro nalezení platných posunů lze použít řadu algoritmů, které se liší složitostí předzpracování i samotného vyhledávání: [^iv003-strings]
| Algoritmus | Preprocessing | Searching |
| ----------------------------------- | ------------------------------------ | ----------------------------------- |
| Brute force / naivní | $0$ | $\mathcal{O}((n - m + 1) \cdot m)$ |
| Karp-Rabin | $\Theta(m)$ | $\mathcal{O}((n - m + 1) \cdot m)$ |
| finite automata | $\Theta(m \cdot \vert \Sigma \vert)$ | $\Theta(n)$ |
| Knuth-Morris-Pratt | $\Theta(m)$ | $\Theta(m)$ |
| Boyer-Moore | $\Theta(m + \vert \Sigma \vert)$ | $\mathcal{O}((n - m + 1) \cdot m)$ |
- $T$ nebo $T\lbrack 1..n \rbrack$ -- text.
- $P$ nebo $P\lbrack 1..m \rbrack$ -- pattern.
- $n$ -- délka textu $T$.
- $m$ -- délka vzorku / podřetězce / patternu $P$.
- $\Sigma$ -- konečná abeceda, ze které je složen text i pattern.
### Brute force / naivní
Prochází všechny pozice v textu a porovnává je s patternem. Pokud se neshodují, posune se o jedno pole dopředu.
Pokud se text neshoduje už v prvním znaku, je složitost lineární. Avšak v nejhorším případě, kdy se pattern shoduje s textem až na poslední znak, je složitost až kvadratická.
```csharp
int Naive(string text, string pattern)
{
int n = text.Length;
int m = pattern.Length;
for (int i = 0; i < n - m + 1; i++)
{
// NB: Substring creates a new string but let's not worry about that.
if (text.Substring(i, m) == pattern)
{
return i;
}
}
return -1;
}
```
- **Složitost**\
Cyklus je proveden $n-m+1$-krát. V každé iteraci se provede přinejhorším až $m$ porovnání. (Zanedbáváme složitost `Substring`, která v C# dělá kopii.)
### Karp-Rabin
Používá hashování. Vytvoří hash z patternu a hashuje i všechny podřetězce délky $m$ v textu. Nejprve eliminuje všechny pozice, kde se hashe neshodují. Pokud se shodují, porovná je znak po znaku.
```csharp
int KarpRabin(string text, string pattern)
{
int n = text.Length;
int m = pattern.Length;
int patternHash = pattern.GetHash();
for (int i = 0; i < n - m + 1; i++)
{
// NB: Assume that `GetHash` is a rolling hash, even though in .NET it's not.
if (text.Substring(i, m).GetHash() == patternHash)
{
if (text.Substring(i, m) == pattern)
{
return i;
}
}
}
return -1;
}
```
- **Hashování**\
Trik spočívá v použití _rolling_ hashovacího algoritmu. Ten je schopný při výpočtu hashe pro $T\lbrack s .. (s + m) \rbrack$ použít hash $T\lbrack s .. (s + m - 1) \rbrack$ s využitím pouze jednoho dalšího znaku $T\lbrack s + m \rbrack$. Přepočet hashe je tedy $\mathcal{O}(1)$.
Jeden takový algoritmus lze získat použitím Hornerova schématu pro evaluaci polynomu. [^horner] Předpokládejme, že $\Sigma = \{0, 1, ..., 9\}$ (velikost může být libovolná), pak pro hash $h$ platí
```math
\begin{align*}
h &= P[1] + 10 \cdot P[2] + 10^2 \cdot P[3] + ... + 10^{m-1} \cdot P[m] \\
&= P[1] + 10 \cdot (P[2] + 10 \cdot (P[3] + ... + 10 \cdot P[m] ... ))
\end{align*}
```
Pokud jsou hashe příliš velké, lze navíc použít modulo $q$, kde $10 \cdot q \approx \text{machine word}$.
- **Složitost**\
Předzpracování zahrnuje výpočet $T \lbrack 1..m \rbrack$ v $\Theta(m)$.
Složitost výpočtu je v nejhorším případě $\mathcal{O}((n - m + 1) \cdot m)$, jelikož je potřeba porovnat všechny podřetězce délky $m$ s patternem.
Tento algoritmus se hodí použít, pokud hledáme v textu celé věty, protože neočekáváme velké množství "falešných" shod, které mají stejný hash jako $P$. V tomto případě je průměrná složitost $\mathcal{O}(n)$.
### Konečné automaty
Složitost naivního algortmu lze vylepšit použitím konečného automatu.
Mějmě DFA $A = (\{0, ..., m\}, \Sigma, \delta, \{0\}, \{m\})$, kde přechodobou funkci definujeme jako:
```math
\delta(q, x) = \text{největší } k \text{ takové, že } P[1..k] \text{ je \textbf{prefix} a zároveň \textbf{suffix} } P[1..q] . x
```
Jinými slovy, $\delta$ vrací delků nejdelšího možného začátku $P$, který se nachází na daném místě (stavu $q$) v řetězci $T$.
Prakticky by příprava přechodové funkce mohla vypadat takto:
```csharp
int[,] CreateAutomaton(string pattern)
{
int m = pattern.Length;
// NB: Assumes that the alphabet is ASCII.
int[,] automaton = new int[m + 1, 256];
for (int q = 0; q <= m; q++)
{
for (int c = 0; c < 256; c++)
{
int k = Math.Min(m + 1, q + 2);
do
{
k--;
}
while (!pattern.Substring(0, q).Equals(pattern.Substring(0, k) + (char)c));
automaton[q, c] = k;
}
}
return automaton;
}
```
Vyhledávání v textu pak bude vypadat takto:
```csharp
int FiniteAutomaton(string text, string pattern)
{
int n = text.Length;
int m = pattern.Length;
int[,] automaton = CreateAutomaton(pattern);
int q = 0;
for (int i = 0; i < n; i++)
{
q = automaton[q, text[i]];
if (q == m)
{
return i - m + 1;
}
}
return -1;
}
```
Tato metoda šetří čas, pokud se pattern v některých místech opakuje. Mějmě například pattern `abcDabcE` a text `abcDabcDabcE`. Tato metoda nemusí začínat porovnávat pattern od začátku po přečtení druhého `D`, ale začne od $P \lbrack 5 \rbrack$ (včetně), protože _ví_, že předchozí část patternu se již vyskytla v textu.
Jinými slovy na indexu druhého `D` je `abcD` nejdelší prefix $P$, který je zároveň suffixem už načteného řetězce.
- **Složitost**\
Vytvoření automatu vyžaduje $\Theta(m^3 \cdot |\Sigma|)$ času, dá se však provést efektivněji než v `CreateAutomaton` a to v čase $\Theta(m \cdot |\Sigma|)$.
Složitost hledání je pak v $\Theta(n)$. [^iv003-strings]
### Knuth-Morris-Pratt (KMP)
KMP představuje efektivnější využití idei z metody konečného automatu:
- Každý stav $q$ je označen písmenem z patternu. Výjimkou je počáteční stav $S$ a koncový stav $F$.
- Každý stav má hranu `success`, která popisuje sekvenci znaků z patternu, a `failure` hranu, která míří do některého z předchozích stavů -- takového, že už načtené znaky jsou největší možný prefix patternu.
V reálné implementaci nejsou `success` hrany potřeba; potřebujeme jen vědět, kam skočit v případě neúspěchu.
```csharp
/// <summary>
/// Computes the longest proper prefix of P[0..i]
/// that is also a suffix of P[0..i].
/// </summary>
int[] ComputeFailure(string pattern)
{
int m = pattern.Length;
int[] fail = new int[m];
int j = 0;
for (int i = 1; i < m; i++)
{
while (j >= 0 && pattern[j] != pattern[i])
{
j = fail[j];
}
// If comparison at i fails,
// return to j as the new starting point.
fail[i] = j;
j++;
}
return fail;
}
int KnuthMorrisPratt(string text, string pattern)
{
int[] fail = ComputeFailure(pattern);
int n = text.Length;
int m = pattern.Length;
// NB: I index from 0 here. Although I use 1..n in the text.
int i = 0;
int j = 0;
for (int i = 0; i < n; i++)
{
while (j >= 0 && text[i] != pattern[j])
{
/*
There can be at most n-1 failed comparisons
since the number of times we decrease j cannot
exceed the number of times we increment i.
*/
j = fail[j];
}
j++;
if (j == m)
{
return i - m;
}
}
return -1;
}
```
> [!WARNING]
> Nejsem si jistý, že ty indexy v kódu výše mám dobře.
> [!NOTE]
> "In other words we can amortize character mismatches against earlier character matches." [^iv003-strings]
- **Složitost**\
Amortizací neúspěšných porovnání vůči úspěšným získáme $\mathcal{O}(m)$ pro `ComputeFailure` a $\mathcal{O}(n)$ pro `KnuthMorrisPratt`.
[^iv003]: [IV003 Algoritmy a datové struktury II (jaro 2021)](https://is.muni.cz/auth/el/fi/jaro2021/IV003/)
[^iv003-strings]: https://is.muni.cz/auth/el/fi/jaro2021/IV003/um/slides/stringmatching.pdf
[^rabin-karp-wiki]: https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm
[^horner]: https://en.wikipedia.org/wiki/Horner%27s_method
[^backtracking]: https://betterprogramming.pub/the-technical-interview-guide-to-backtracking-e1a03ca4abad

View File

@ -322,7 +322,7 @@ description: "TODO"
### Změna struktury sítě
> [!IMPORTANT]
> Modifikace meshů mají značný přesah do otázky [Křivky a povrchy](../krivky-a-povrchy/) a taky [Pokročilá počítačová grafika](../vph01_pokrocila_grafika/)
> Modifikace meshů mají značný přesah do otázky [Křivky a povrchy](../szp05_krivky_a_povrchy/) a taky [Pokročilá počítačová grafika](../vph01_pokrocila_grafika/)
- **Překlápění hrany / edge flip**\
Lokální změna, která nahradí hranu $(b,c)$ hranou $(a,d)$. Trojúhelníky $(a,b,c)$ a $(b,d,c)$ se stanou $(a,d,c)$ a $(a,b,d)$. [^pa010-2021]

View File

@ -135,6 +135,9 @@ Hledá nejkratší cesty z jednoho vrcholu do všech ostatních.
- Využívá relaxaci hran.
- Funguje i na grafech se zápornými hranami.
- Má časovou složitost $\mathcal{O}(\lvert V \rvert \cdot \lvert E \rvert)$.
- Udělá max $V-1$ iterací, protože nejdelší cesta může mít max $V-1$ hran (přes žádný vertex nepůjdeš dvakrát).
- _Modifikace_: Po $V-1$ by už nejkratší cesty měly být nalezeny. Udělejme ještě jednu iteraci - pokud se hodnoty změní, pak nejkratší cesta obsahuje negativní cyklus. Viz python kód dole.
- _Modifikace_: Early stop - pokud se 2 interace po sobě hodnoty vrcholů nezmění, pak jsme našli nejkratší cesty.
```python
def bellmanford(graph: List[List[Tuple[int, int]]], s: int) \
@ -226,9 +229,9 @@ Dijkstrův algoritmus lze optimalizovat, pokud nás zajímá jen nejkratší ces
- **Fundamental cutset / řez**\
Fundamental cutset je množina hran $D$ v grafu $G$ taková, že přidáním libovolné hrany $e \in D$ získáme kostru.
- **Red rule**\
Najdi cyklus bez červených hran, vyber v něm **neobarvenou** hranu s **nejvyšší** cenou a obarvi ji červeně.
Najdi cyklus bez červených hran, vyber v něm **neobarvenou** hranu s **nejvyšší** cenou a obarvi ji červeně. Červená hrana znamená odebrání z finální kostry.
- **Blue rule**\
Najdi řez bez modrých hran, vyber v něm **neobarvenou** hranu s **nejmenší** cenou a obarvi ji modře.
Najdi řez bez modrých hran, vyber v něm **neobarvenou** hranu s **nejmenší** cenou a obarvi ji modře. Modrá hrana znamená přidání do finální kostry.
- **Greedy algoritmus**\
Nedeterministicky aplikuj red rule a blue rule, dokud to jde (stačí $n-1$ iterací). Modré hrany tvoří MST.
- **Jarníkův / Primův algoritmus**\
@ -305,7 +308,7 @@ Dijkstrův algoritmus lze optimalizovat, pokud nás zajímá jen nejkratší ces
Je funkce $f: E \rightarrow \mathbb{R}^+$, která splňuje:
- podmínku kapacity: $(\forall e \in E)(f(e) \ge 0 \land f(e) \leq c(e))$
- _tok hranou je nezáporný a nepřevyšuje povolennou kapacitu_
- _tok hranou je nezáporný a nepřevyšuje povolenou kapacitu_
- podmínku kontinuity: $(\forall v \in V \setminus \{s, t\})(\sum_{e \in \delta^+(v)} f(e) = \sum_{e \in \delta^-(v)} f(e))$
- _tok do vrcholu je stejný jako tok z vrcholu_
@ -332,7 +335,7 @@ Dijkstrův algoritmus lze optimalizovat, pokud nás zajímá jen nejkratší ces
$$
- **Augmenting path $P$**\
Jednoduchá $s \rightsquigarrow t$ cesta v residuální síti $G_f$.
Jednoduchá $s \rightsquigarrow t$ cesta v residuální síti $G_f$. Cesty hledáš buď BFS nebo DFS - každou iteraci si prostě nějakou vybereš.
> [!NOTE]
> T.j. cesta která může jít i proti směru toku $f$.
@ -518,11 +521,11 @@ Dijkstrův algoritmus lze optimalizovat, pokud nás zajímá jen nejkratší ces
**Srovnání algoritmů Ford-Fulkerson a Push-Relabel**
| Ford-Fulkerson |
| ----------------------- | ------------------------------------ |
| Push-Relabel (Goldberg) | global character |
| local character | update flow along an augmenting path |
| update flow on edges | flow conservation |
| Ford-Fulkerson | Push-Relabel (Goldberg) |
| ------------------------------------ | ----------------------- |
| global character | local character |
| update flow along an augmenting path | update flow on edges |
| flow conservation | preflow |
## Maximální párování v bipartitních grafech
@ -556,9 +559,6 @@ Dijkstrův algoritmus lze optimalizovat, pokud nás zajímá jen nejkratší ces
![width=300](./img/szp07_mcm_03.png)
[^ib000]: [IB000 Matematické základy informatiky (podzim 2022)](https://is.muni.cz/auth/el/fi/podzim2022/IB000/um/)
[^ib002]: [IB002 Algoritmy a datové struktury (jaro 2020)](https://is.muni.cz/auth/el/fi/jaro2020/IB002/um/)
[^ib003]: [IV003 Algoritmy a datové struktury II (jaro 2021)](https://is.muni.cz/auth/el/fi/jaro2021/IV003/um/)
[^matching]: [Wikipedia: Párování grafu](https://cs.wikipedia.org/wiki/P%C3%A1rov%C3%A1n%C3%AD_grafu)
[^mcm]: [Wikipedia: Maximum cardinality matching](https://en.wikipedia.org/wiki/Maximum_cardinality_matching)

View File

@ -4,7 +4,7 @@ description: "TODO"
---
> [!WARNING]
> Tato je stará verze otázky. Nová verze: [Grafické principy ve vývoji her](../VPH01_graficke_principy_ve_vyvoji_her).
> Tato je stará verze otázky. Nová verze: [Grafické principy ve vývoji her](../vph01_graficke_principy_ve_vyvoji_her).
> [!NOTE]
> Techniky aproximace objektů. Renderování objemových dat (bodový mrak, techniky rekonstrukce povrchů, přímé renderování objemu). Lokální a globální modely nasvícení. Renderování založené na fyzikálních modelech (PBR). Techniky renderování stínů.

View File

@ -4,7 +4,7 @@ description: "TODO"
---
> [!WARNING]
> Tato je stará verze otázky. Nová verze: [Fyzikální principy ve vývoji her](../VPH02_fyzikalni_principy_ve_vyvoji_her).
> Tato je stará verze otázky. Nová verze: [Fyzikální principy ve vývoji her](../vph02_fyzikalni_principy_ve_vyvoji_her).
> [!NOTE]
> Příprava a vývoj scény, grayboxing, zástupné modely (placeholders). Optimalizace výkonu vykreslování (úrovně detailů, odstřelování objektů, MIP mapy). Využití shaderů pro efekty ve hrách. Sledování paprsků, objekty pro detekci kolizí, fyzika hadrové panenky.

View File

@ -50,6 +50,11 @@ for f in files:
search = f"../{url}"
if search in l:
lines[i] = l.replace(search, f"../{links[url].lower()}")
l = lines[i]
for filename in links.values():
if filename in l:
lines[i] = l.replace(filename, filename.lower())
with open(os.path.join(TARGET_DIR, f), "w") as file:
file.writelines(lines)