Grafy a grafové algoritmy
Note
Reprezentace grafů. Souvislost grafu, rovinné grafy. Prohledávání grafu do šířky a do hloubky, nejkratší vzdálenosti, kostry, toky v sítích. Algoritmy: Bellman-Ford, Dijkstra, Ford-Fulkerson, Push-Relabel, maximální párování v bipartitních grafech.
> IB000, IB002, IV003
Tip
Tahle otázka má solidní překryv s bakalářskými otázkami Grafy a Grafové problémy.
Terminologie
Section titled “Terminologie”-
Graf
Dvojice kde:- je množina vrcholů; ,
- je množina hran; ,
- hrana je dvojice vrcholů .
-
Váha grafu
Váha grafu je součet vah hran grafu . -
Bipartitní graf
Graf jehož vrcholy lze rozdělit do dvou disjunktních množin tak, že všechny hrany vedou z jedné množiny do druhé.Example of bipartite graph without cycles by Watchduck
-
(Silná) souvislost grafu / (strongly) connected graph
Graf je souvislý, pokud pro každé dva vrcholy existuje cesta z do . -
Slabá souvislost grafu / weakly connected graph
Graf je slabě souvislý, pokud je souvislý jeho podgraf vzniklý odebráním orientace hran.Je souvislý alespoň, pokud zapomeneme, že hrany mají směr?
-
Silně souvislá komponenta / strongly connected component
Silně souvislá komponenta grafu je jeho maximální podgraf takový, že je silně souvislý. Jinými slovy pro každé dva vrcholy existuje cesta z do . -
Planární / rovinný graf
Graf je planární, pokud se dá nakreslit do roviny tak, že se žádné dvě hrany nekříží.Platí v nich Eulerova formule:
Kde je počet stěn — oblastí ohraničených hranami.
Vrcholy planárního grafu lze vždy obarvit 4 barvami tak, že žádné dva sousední vrcholy nebudou mít stejnou barvu.
-
(Hranový) řez / (edge) cut
Množina hran taková, že po odebrání hran se graf rozpadne na více komponent — není souvislý.Analogicky se definuje i vrcholový řez / vertex cut.
Reprezentace grafů
Section titled “Reprezentace grafů”-
Seznam následníků / adjacency list
Pro každý vrchol máme seznam (např. dynamic array nebo linked list) jeho následníků.Zabírá paměti.
-
Matice sousednosti / adjacency matrix
Máme matici velikosti kde pokud existuje hrana mezi a , jinak .Dá se pěkně použít k uložení vah.
-
Matice incidence / incidence matrix
Máme matici velikosti kde pokud je vrcholem hrany , jinak .Dá se z ní pěkně určit stupeň vrcholu.
Prohledávání grafu
Section titled “Prohledávání grafu”Prohlédávání do šířky / breadth-first search (BFS)
Section titled “Prohlédávání do šířky / breadth-first search (BFS)”Od zadaného vrcholu navštíví nejprve vrcholy vzdálené 1 hranou, poté vrcholy vzdálené 2 hranami, atd.
- Prohledávání po “vrstvách”.
- Je implementovaný pomocí fronty (queue / FIFO).
- Časová složitost je .
def dfs(graph: List[List[bool]], stamps: List[int], vertex: int) -> None: if stamps[vertex] == -1: stamps[vertex] = 0 stamp = stamps[vertex] for i in range(0, len(graph)): if graph[vertex][i] and stamps[i] != -1: stamps[i] = stamp + 1 dfs(graph, stamps, i)Prohlédávání do hloubky / depth-first search (DFS)
Section titled “Prohlédávání do hloubky / depth-first search (DFS)”Od zadaného vrcholu rekurzivně navštěvuje jeho nenavštívené následníky.
- Prohledání po “slepých uličkách”.
- Vynořuje se teprve ve chvíli, kdy nemá kam dál (backtrackuje).
- Je implementovaný pomocí zásobníku (stack / LIFO).
- Časová složitost je .
def bfs(graph: List[List[bool]], stamps: List[int], vertex: int) -> None: stamp = 0 queue = deque() queue.append(vertex) while len(queue) > 0: current = queue.popleft() stamps[current] = stamp stamp += 1 for i in range(0, len(graph)): if graph[current][i] and stamps[i] == -1: queue.append(i)Nejkratší vzdálenosti
Section titled “Nejkratší vzdálenosti”Problém nalezení buď nejkratší cesty mezi dvěma vrcholy nebo nejkratší cesty z jednoho vrcholu do všech ostatních.
- Relaxace hrany
Zkrácení vzdálenosti k vrcholu průchodem přes vrchol . Musí platit . Hrana je v takovém případě napjatá.
Bellman-Fordův algoritmus
Section titled “Bellman-Fordův algoritmus”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 .
- Udělá max iterací, protože nejdelší cesta může mít max hran (přes žádný vertex nepůjdeš dvakrát).
- Modifikace: Po 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.
def bellmanford(graph: List[List[Tuple[int, int]]], s: int) \ -> Tuple[bool, List[int], List[int]]: # graph is an adjacency list of tuples (dst, weight) distance = [float('inf') for i in range(0, len(graph))] distance[s] = 0 parent = [-1 for i in range(0, len(graph))]
# relax all edges |V| - 1 times for _ in range(1, len(graph)): for u in range(0, len(graph)): for edge in graph[u]: (v, w) = edge if distance[u] + w < distance[v]: distance[v] = distance[u] + w parent[v] = u
# check for negative cycles for u in range(0, len(graph)): for edge in graph[u]: (v, w) = edge if distance[u] + w < distance[v]: return (False, None, None)
return (True, distance, parent)Dijkstrův algoritmus
Section titled “Dijkstrův algoritmus”Hledá nejkratší cesty z jednoho vrcholu do všech ostatních.
- Je podobný BFS, ale používá prioritní frontu.
- Funguje pouze na grafech bez záporných hran.
Tip
Složitost závisí na implementaci prioritní fronty. Je to insertů, hledání nejmenšího prvku, snížení priority.
Note
Implementace níže používá pole (resp. Pythoní list), tedy složitost je , jelikož hledání minima je lineární.
def dijkstra(graph: List[List[Tuple[int, int]]], s: int) \ -> Tuple[List[int], List[int]]: # graph is an adjacency list of tuples (dst, weight) distance = [float('inf') for i in range(0, len(graph))] distance[s] = 0 parent = [-1 for i in range(0, len(graph))]
queue = list(range(0, len(graph))) while len(queue) > 0: u = min(queue, lambda v: distance[v]) queue.remove(u) for edge in graph[current]: (v, w) = edge if distance[u] + w < distance[v]: distance[v] = distance[u] + w parent[v] = u return (distance, parent)V binární haldě by to bylo a ve Fibonacciho haldě .
Dijkstrův algoritmus lze optimalizovat, pokud nás zajímá jen nejkratší cesta mezi dvěma konkrétními vrcholy:
-
Funkce vrátí výsledek, jakmile je cílový vrchol vytažen z fronty.
-
Můžeme hledat zároveň ze začátku a konce pomocí dvou front a skončit, jakmile se někde potkají.
-
Můžeme přidat potenciál — dodatečnou heuristickou váhu.
Important
Téhle variantě se říká A* (A star). Věnuje se mu část otázky Umělá inteligence v počítačových hrách.
Kostry
Section titled “Kostry”-
Spanning tree / kostra
Kostra grafu je podgraf takový, že je je strom. -
Minimum spanning tree (MST) / minimální kostra
Je kostra grafu s nejmenší možnou váhou. Tedy pro každou kostru grafu : -
Fundamental cycle
Fundamental cycle je cyklus v grafu takový, že odebráním libovolné hrany získáme kostru. -
Fundamental cutset / řez
Fundamental cutset je množina hran v grafu taková, že přidáním libovolné hrany 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ě. Č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. Modrá hrana znamená přidání do finální kostry. -
Greedy algoritmus
Nedeterministicky aplikuj red rule a blue rule, dokud to jde (stačí iterací). Modré hrany tvoří MST. -
Jarníkův / Primův algoritmus
Speciální případ greedy algoritmu, kdy aplikujeme pouze blue rule. Princip:- Vyber libovolný vrchol a přidej ho do kostry .
- Opakuj krát:
- Vyber hranu s nejmenší cenou, která má právě jeden vrchol v .
- Přidej druhý vrchol do .
Složitost: použijeme binární haldu
- Inicializace ( jako cena hrany mezi prázdnou kostrou a každým vrcholem):
- Odstranění minima z binární haldy pro každý vrchol ve :
- Procházení každé hrany z a snižování ceny:
- Celková složitost:
- S Fibonacciho haldou jde zlepšit na:
-
Kruskalův algoritmus
Princip: Seřaď hrany podle ceny vzestupně. Postupně přidávej hrany do kostry, vynechej ty, které by vytvořily cyklus.- Seřad hrany podle ceny vzestupně.
- Použij union-find na udržování komponent grafu.
- Procházej hrany postupně. Pokud oba konce hrany patří do různých množin, přidej ji.
Je to speciální případ greedy algoritmu.
Složitost:
- Inicializace union-findu:
- Seřazení hran:
- Pro každou hranu provádíme dvakrát
find() a eventuálněunion(): - Celková složitost:
-
Borůvkův algoritmus
Je “paralelní”. Buduje modré stromy ze všech vrcholů naráz.- Pro každý vrchol inicializuj modrý strom.
- Dokud nemáš jen jeden modrý strom, opakuj fázi:
- Pro každý modrý strom najdi nejlevnější odchozí hranu a přidej ji (propojíš tak dva stromy).
Je to speciální případ greedy algoritmu, který spamuje jen blue rule.
Složitost:
- Počet komponent v první fázi: .
- V každé fázi se zmenší počet komponent na polovin. Tím pádem bude fází.
- Každá fáze zabere času, protože procházíme všechny hrany.
- Celková složitost:
Tip
Kruskal sice taky buduje stromy na více místech najednou, ale není “paralelní”, protože minimalita kostry spoléhá na to, že hrany jsou seřazené. Borůvka takový požadavek nemá, a proto je paralelizovatelnější.
Složitosti algoritmů
| Algoritmus | Časová složitost | Prostorová složitost |
|---|---|---|
| Jarník (Prim) s prioritní frontou | ||
| Jarník (Prim) s Fibonacciho haldou | ||
| Kruskal | ||
| Borůvka |
Toky v sítích
Section titled “Toky v sítích”-
Síť toků / flow network
Je čtveřice , kde:- je orientovaný graf,
- je zdroj / source,
- je cíl / stok / sink; ,
- je funkce udávající kapacitu hran.
-
Network flow / tok
Je funkce , která splňuje:- podmínku kapacity:
- tok hranou je nezáporný a nepřevyšuje povolenou kapacitu
- podmínku kontinuity:
- tok do vrcholu je stejný jako tok z vrcholu
- podmínku kapacity:
-
Hodnota toku
Ford-Fulkerson
Section titled “Ford-Fulkerson”-
Residual network
Síť, která vzniká, když je už část kapacity hrany využívána tokem . Umožnuje algoritmům změnit přechozí rozhodnutí a získat využitou kapacitu zpět.Je to pětice , kde
- ,
- pokud , ,
-
Augmenting path
Jednoduchá cesta v residuální síti . 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 .
Bottleneck kapacita je nejmenší kapacita hran v augmenting path .
To krásné na augmenting cestách je, že pro flow a augmenting path v grafu , existuje tok takový, že . Nový tok lze získat takto:
*Augment*(f, c, P){delta = bottleneck(P)*foreach*(e in P){*if*(e in E){f[e] = f[e] + delta}*else*{f[reverse(e)] = f[reverse(e)] - delta}}*return* f} -
Algoritmus Ford-Fulkerson
Hledá maximální tok. Augmentuje cestu v residuální síti dokud to jde.- pro každou .
- Najdi cestu v reziduální síti .
- Augmentuj tok podél .
- Opakuj dokud se nezasekneš.
*Ford-Fulkerson*(G){*foreach* (e in E){f(e) = 0}G_f = reziduální síť vzniklá z G vzhledem k toku f*while* (existuje s ~> t cesta v G_f){f = Augment(f, c, P)Updatuj G_f}*return* f}
Push-Relabel
Section titled “Push-Relabel”-
Pre-flow
Ne-tak-docela tok.Funkce taková, že
- platí kapacitní podmínka: ,
- platí relaxováné zachování toku:
-
Overflowing vertex
Takový vertex , do kterého více přitéká než odtéká. -
Excess flow
To, co je v overflowing vertexu navíc. -
Height function
Funkce . Řekneme, že je kompatibilní s preflow , právě když-
source: ,
-
sink: ,
-
height difference: .
Note
Pokud mezi dvěma vrcholy v reziduální síti vede hrana, pak je nejvýše o jednu úroveň výš než .
-
-
Push operace
Pro (reziduálně-grafovou) hranu se pokusí přesunout excess flow z do , aniž by porušil (reziduální) kapacitu .// Assumptions: e_f[v] > 0, c_f( (v, w) > 0) > 0, h[v] > h[w]*Push*(f, h, v, w){delta_f = min(e_f[v], c_f(v, w))*if*( (v, w) in E)f[(v, w)] += delta_f*else*f[(w, v)] -= delta_fe_f[v] -= delta_fe_f[w] += delta_f} -
Relabel operace
Zvýší výšku natolik, aby neporušil kompatibilitu s .// Assumptions:// - v is overflowing: e_f[v] > 0// - all residual neighbors of v the same height or higher:// forall (v, w) in E_f: h[v] \<= h[w]*Relabel*(f, h, v){h[v] = 1 + min(h[w] | (v, w) in E_f)} -
Algoritmus Push-Relabel (Goldberg-Tarjan)
Hledá maximální tok.Princip: Pokud je nějaký vrchol overflowing, tak ho pushni nebo relabeluj. Pokud ne, tak jsi našel maximální tok.
*Push-Relabel*(V, E, s, t, c){// initialize preflow -- default values*for*(v in V){h[v] = 0 // height functione_f[v] = 0 // excess flow}n = |V|h[s] = n*for*(e in E){f[e] = 0 // (pre)flow}// initialize preflow -- saturate connections from s*for*( (s, v) in E){f[(s, v)] = c(s, v) // preflow maxes out all capacitye_f[v] = c(s, v) // presume all of it excesse_f[s] -= c(s, v) // yes, it will be negative}// the juicy part*while*(_any vertex is overflowing_){v = _an overflowing vertex_ (has e_f[v] > 0)*if*(v _has a neighbor_ w _in_ G_f _such that_ h(v) > h(w)){*Push*(f, h, v, w)}else{*Relabel*(f, h, v)}}*return* f}Korektnost: V průběhu výpočtu platí:
- Výška vrcholu nikdy neklesá.
- Pre-flow a výšková funkce jsou kompatibilní.
Složitost:
- Nejvýše Relabelů.
- saturujících Push.
- nesaturujících Push.
- Relabel i Push jsou v .
- Celkem: .
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 | preflow |
Maximální párování v bipartitních grafech
Section titled “Maximální párování v bipartitních grafech”-
Párování / matching
Množina taková, že žádné dvě hrany v nemají společný vrchol. 1Prázdná množina je párováním na každém grafu. Graf bez hran má pouze prázdné párování.
Příklad párování, které je zároveň maximální by RRPPGG

-
Maximální párování
Takové párování, které má nejvyšší počet hran. Graf může mít několik maximálních párování. -
Perfektní párování
Takové párování, které páruje všechny vrcholy grafu. Každé perfektní párování je zároveň maximální. -
Maximum cardinality matching (MCM) v bipartitním grafu
Problém nalezení maximálního párování v grafu. Ve speciálním případě, kdy graf je bipartitní, se tento problém dá převést na problém nalezení maximálního toku v síti: 2-
Mejmě bipartitní graf .

-
Přidej zdroj a hranu pro každý vrchol z .
-
Přidej stok a ranu pro každý vrchol z .

-
Každé hraně dej kapacitu 1.
-
Spusť algoritmus Ford-Fulkerson.

-