Goldbergův algoritmusGoldbergův algoritmus hledá maximální tok v síti v čase . Patří do třídy algoritmů s operacemi přemístění přebytku a zvedání vrcholu na nalezení maximálního toku, které mají . Pro husté grafy je efektivnější než Edmonds-Karpův algoritmus, který má časovou složitost . V anglické literatuře se Goldbergův algoritmus též nazývá algoritmem preflow-push nebo relabel-to-front. AlgoritmusJe dána síť s kapacitou z vrcholu u do vrcholu v danou jako , zdroj z a spotřebič s. Chceme najít maximální tok, který můžeme poslat přes síť ze z do s. Na vrcholech se provádějí dva typy operací: přemístění přebytku a zvedání vrcholu. Během algoritmu dále udržujeme:
Z důvodů zjednodušení zápisu popisu algoritmu a implementace přidáme do grafu dočasně pro každou hranu opačně orientovanou hranu s nulovou kapacitou. Během výpočtu pak budeme vyžadovat, aby byl pro každou hranu vždy splněn vztah . Přidání pomocných hran lze samozřejmě udělat pouze v případě, že graf neobsahuje orientované smyčky. V případě existence smyček bychom museli postupovat o trochu opatrněji, rozmyšlení tohoto zřídkakdy se vyskytujícího případu ponecháme čtenáři za domácí cvičení :-). Na hodnoty f(u,v) dále klademe během výpočtu následující podmínky:
Zobrazení splňující tyto podmínky nenazýváme přípustným tokem, tak jak je používán například ve Ford-Fulkensonově metodě, ale pouze vlnou. Rozdíl je v tom, že vlna povoluje kladný přebytek. Vlnu používáme pouze během výpočtu, výstupem algoritmu je přípustný tok (speciální případ vlny). Všimněte si, že poslední podmínka pro přebytek je odvozená z korespondující podmínky pro přípustný tok v běžné síti. Pozorujeme, že nejdelší možná cesta ze z do s je vrcholů dlouhá. Proto pro každý přípustný tok existuje přiřazení výšky vrcholům splňující, že a , a je-li tok hranou z u do v kladný, pak . Během přemisťování přebytku vrcholů se vlna chová stejně jako vodní vlna v přírodě—přebytek se přelévá pouze z vrcholů s větší výškou do níže položených. Na rozdíl od Ford-Fulkersonova algoritmu, tok (vlna) v síti nemusí nutně být přípustným tokem během výkonu algoritmu. Zjednodušeně, nejprve naplníme všechny hrany vedoucí ze zdroje. Dále během výpočtu zvyšujeme výšky vrcholů (kromě z a s) a přebytky se jako vlna šíří mezi vrcholy, dokud veškeré přebytky nedosáhnou spotřebiče. Poté pokračujeme ve zvyšování výšky vnitřních vrcholů sítě, až dokud se přebytky, které nedosáhly spotřebiče, nevrátí zpět do zdroje. Vrchol může dosáhnout nejvýše výšky , protože nejdelší možná cesta z libovolného vrcholu kromě s zpátky do z je dlouhá a . Výška s se udržuje na 0. Přemístění přebytkuPřemístění přebytku z u do v znamená přemístit část přebytku v u do v. Pro přemísťování přebytku musí být splněny tři podmínky:
Posíláme pouze množství rovné . Zvedání vrcholůZvedání vrcholu u je zvětšení jeho výšky, dokud není vyšší než alespoň jeden vrchol s volnou kapacitou. Podmínky pro zvedání vrcholu:
Při zvednutí vrcholu u přiřadíme nejnižší hodnotu tak, aby pro nějaké v, kde . Algoritmus přemísťování přebytku a zvedání vrcholuAlgoritmus přemísťování přebytku a zvedání vrcholu má ve všeobecnosti tento postup:
Obecná časová složitost pro algoritmy používající tento postup je . Golbergův algoritmus přidává další modifikace, které časovou složitost zlepší na . Odstranění přebytkuOdstranění přebytku vrcholu u znamená:
Goldbergův algoritmusGoldbergův algoritmus určuje pořadí operací přemístění přebytku a zvedání vrcholu:
Časová složitost Goldbergova algoritmu je (důkaz vynechán). Ukázková implementaceImplementace v programovacím jazyku Python verze 2: def goldberg(C, zdroj, spotrebic): n = len(C) # C je matice kapacit F = [[0] * n for _ in xrange(n)] # zbytková kapacita z u do v je C[u][v] - F[u][v] vyska = [0] * n # výška vrcholu prebytek = [0] * n # rozdíl toku do a z vrcholu vyzkousen = [0] * n # sousedi vyzkoušení od posledního zvedání vrcholu # "seznam" vrcholů seznam = [i for i in xrange(n) if i != zdroj and i != spotrebic] def zvedni(u, v): posli = min(prebytek[u], C[u][v] - F[u][v]) F[u][v] += posli F[v][u] -= posli prebytek[u] -= posli prebytek[v] += posli def zvedni(u): # najdi nejmenší novou výšku a udělej přemístění přebytku možným, # jestli je vůbec možné něco zvednout min_vyska = vyska[u] for v in xrange(n): if C[u][v] - F[u][v] > 0: min_vyska = min(min_vyska, vyska[v]) vyska[u] = min_vyska + 1 def vyprazdni(u): while prebytek[u] > 0: if vyzkousen[u] < n: # zkontroluj dalšího souseda v = vyzkousen[u] if C[u][v] - F[u][v] > 0 and vyska[u] > vyska[v]: zvedni(u, v) else: vyzkousen[u] += 1 else: # zkontrolovali jsme všechny sousedy. musíme ho zvednout zvedni(u) vyzkousen[u] = 0 vyska[zdroj] = n # nejdelší cesta ze zdroje do spotřebiče je kratší než n prebytek[zdroj] = Inf # pošli tolik toku, kolik se jen dá sousedovi zdroje for v in xrange(n): zvednout(zdroj, v) p = 0 while p < len(seznam): u = seznam[p] old_vyska = vyska[u] vyprazdni(u) if vyska[u] > old_vyska: seznam.insert(0, seznam.pop(p)) # přesuň na začátek seznamu p = 0 # začni ze začátku seznamu p += 1 return sum([F[zdroj][i] for i in xrange(n)]) Všimněte si, že tato implementace není moc efektivní. Je pomalejší než Edmonds-Karpův algoritmus dokonce pro hodně husté grafy. Pro zrychlení můžete udělat nejméně dvě věci:
Související článkyReference
|
Portal di Ensiklopedia Dunia