Effiziente Algorithmen 2 - [choerbaert.org [ˈχœrbɛrt]]

Werbung
Effiziente Algorithmen 2
Vorlesung von Prof. Dr. Manfred Kunde
Zusammenfassung von Alexander Heinlein
01 – Einführung
– Turing-Maschine (TM): prinzipielle Berechenbarkeit, jedoch unhandlich für reale Probleme
– RAM (Random Access Maschine): kann alles wie TM, relativ dicht an realen Computern
– read-only Programmspeicher, Register, Speicher
– Befehle: Transportbefehle (Speicherzugriffe), Sprungbefehle, arithmetische B., Indexb. (Adressenberechnung)
– Kosten: elementare Operationen konstant / O(1), logarithmisches Kostenmaß proportional zur Zahlenlänge
02 – Sortieren / Auswählen
Allgemeine Sortierverfahren
– Sortierproblem: n Objekte R1, …, Rn mit Ri = (Si, Ii) für Si = Schlüssel, Ii = Information
– zugehörige Information spielt keine Rolle, Trennung von Schlüssel/Information (nur Bewegung der Schlüssel)
– MaxSort:
– Suchen des kleinsten/größten Elementes, vertauschen mit ersten usw.
– Laufzeit auf RAM: ~3.25n2 + 6.75n
– mittlere Laufzeit (bei Gleichverteilung): ~2.5n2 + 3(n + 1)ln(n) + 4.5n
– Schlüsselvergleiche: ϴ(n2)
– worst / average case: (n – 1) + (n – 2) + ... + 3 + 2 + 1 = n(n – 1)/2
– BubbleSort: Schlüsselvergleiche: n(n – 1)/2
– HeapSort (Bottom-Up-HeapSort):
– Feld als binärer Baum
– Heap-Bedingung: S[i] ≥ S[2i] und S[i] ≥ S[2i + 1]
– versickern/absinken: Knoten mit größerem seiner beiden Nachfolger vertauschen, falls dieser größer ist als er
selbst
– Konstruktion MaxHeap: bei Arrayposition (n/2) – 1 anfangen, schauen ob Heap-Bedingung erfüllt, falls nicht,
mit größeren der beiden Elemente tauschen
– Sortieren: größtes Element nehmen (Wurzel) und mit Ende des Arrays tauschen, schauen ob Heap-Bedingung
für Wurzel erfüllt, falls nein erneuten Heap bauen (Element versickern lassen)
– Schlüsselvergleiche: 1.5nlog(n) + O(n), im Mittel: nlog(n) + O(n)
– best/worst case: O(nlog(n))
– QuickSort:
– Schlüsselvergleiche: 2(n + 1)ln(n + 1) – 2/3(n + 1) und ϴ(nlog(n)) sonstige Operationen
– best case: O(nlog(n)), worst case: O(n2)
– Clever QuickSort:
– Nachteil QuickSort: Wahl des Pivotelement kann zu leeren Teilmenge führen wenn größtes/kleinstes
Element gewählt
– wähle zufällig 3 Indizes, Bestimme Median → im Schnitt 8/3 Vergleiche
– MergeSort: Menge in zwei Teilfolgen teilen, Mergesort jeweils rekursiv aufrufen, am Ende sortierte Teilfolgen im
Reißverschlussverfahren zusammenfügen
– Schlüsselvergleiche: nlog(n) – 2log(n) + 1 und O(nlog(n)) sonstige Operationen
– best/worst/average case: O(nlog(n))
– jeder Algorithmus, der zwei beliebig sortierte Folgen zu einer sortierten Folge vereinigt, benötigt im schlechtesten
Fall n – 1 Vergleiche
MaxSort HeapSort Bottom-up-HS QuickSort
CleverQS MergeSort
Vergleiche
Laufzeit
Platzbedarf
Zugriff
worst case
average
n2/2
2nlog(n)
1.5nlog(n)
n2/2
n2/2
nlog(n)
2
2nlog(n)
nlog(n)
1.3nlog(n)
1.18nlog(n)
nlog(n)
n /2
worst case
2.5n
2
20nlog(n)
?
O(n )
?
12nlog(n)
average
2.5n2
20nlog(n)
?
2nlog(n)
?
12nlog(n)
n+c
n+c
n+c
wahlfrei
wahlfrei
wahlfrei
1
2
n + log(n) + c n + log(n) + c
wahlfrei
wahlfrei
2n
sequentiell
Sortieren strukturierter Schlüssel
– Umgehung der unteren Schranke nlog(n) für vergleichsbasierte Suchverfahren durch Nutzen der Schlüssel als Index
– BucketSort:
– Feld K mit m Fächern, n Elemente, wirf Element xi in Fach K[xi], xi sind alle Wörter der gleichen Länge k
– Sortiere nach k-ten Buchstaben, dann nach (k – 1)-ten Buchstaben usw. → k Läufe über der Wortmenge
– jeder Lauf O(n + m) → O(k(n + m))
Sortieren reeler Zahlen durch Verteilen
– HybridSort:
– k Fächer, Element xi (für xi ∈ (0, 1]) wird in Fach ⌈k · xi⌉ gelegt, jedes Fach wird mit HeapSort sortiert
– worst case: O(nlog(n)) durch HeapSort, best/average case: O(n) wenn xi unabhängig und gleichverteilt
Auswahlproblem
– S1, …, Sn paarweise verschiedene Elemente aus geordneter Menge, finde i-t-kleinstes Element
– Spezialfälle: bestimme Maximum/Minimum (n – 1 Vergleiche)
– QuickSelect:
– Methode: Median der Mediane
– wähle Pivotelement p (3 Werte wählen, davon Median bestimmen), tausche wie bei QuickSort so, dass links
alle Elemente kleiner als Pivot und rechts alle größer, p ist nun an Stelle j
– wenn i = j, dann ist p nun an Stelle i, fertig
– falls i < j: sortiere linke Teilfolge, andernfalls rechte
– average case: O(n), worst case: O(n2) (wenn ≤ 100 Elemente, statt QuickSelect einfach sortieren)
03 – Verwaltung von Mengen
Fundamentale Operationen auf Mengen
– member(a, S) / iselement(a, S): a ∈ S? → Rückgabewert 1/0
– insert(a, S), delete(a, S)
– union(S1, S2, S3): S3 = S1 ∪ S2 wobei S1 ∩ S2 = Ø
– union(a, b): a = a ∪ b
– find(a): Name der Menge, die a enthält (undefiniert falls a in mehreren Mengen)
– split(a, S): S ist linear geordnete Menge mit ≤, partitioniert S in S1 = { b | b ∈ S, b ≤ a } und S2 = { b| b ∈ S, b > a }
– min(S): kleinstes Element bezüglich „≤“
Datenstrukturen
– zur Unterstützung der fundamentalen Operationen
– min, insert, delete: Priority-Queues (Vorrangswarteschlangen)
– insert, delete, member: Wörterbuch
– insert, delete, member, union: mergeable Heap
– union, find: Union-Find-Struktur
Union-Find-Algorithmus
– Darstellung von Mengen: Pfeil zeigt auf unmittelbaren Vorgänger pred(x), Name des Baumes (Menge) ist Name der
Wurzel, Größe g(T): Anzahl der Knoten
– knotenbeschriftete Bäume, Wurzel hat sich selber zum Vorgänger
– union zum Vereinigen zweier Mengen, find zum Bestimmen in welcher Menge ein Element sich befindet
– Baum kann zur Liste werden: find in O(n), union in O(1)
– sonst: find in O(log(n)), union in O(1)?
– union(e, f): pred(f) = e; g(e) = g(e) + g(f)
– find(y): while pred(y) ≠ y do y = pred(y); done; return y
– gewichtete Vereinigungsregel: kleineren Baum in Wurzel des größeren anhängen
– Lemma: Baum mit Höhe h, der nach der gewichteten Vereinigungsregel erzeugt wurde, enthält mindestens 2h
Knoten → find in höchstens O(log(n)) da alle Bäume höchstens ⌊log(n)⌋ Elemente haben
– Kosten der find-Operation: Höhe der Bäume, Ziel: Beschleunigen der find-Operation
– Lösung 1: alle Knoten an Wurzel hängen, dadurch billiges find, aber teures union (idR häufigeres find als union)
– Lösung 2: Verkürzung der Pfadlängen während find-Operationen: Pfadkompression
– Aufteilungsmethode: jeder Knoten zeigt auf bisherigen Großvater → Aufteilung des Suchpfades in zwei Pfade
ungefähr gleicher Länge
– Halbierungsmethode: jeder zweite Knoten an Großvater gehängt, beginnend mit gesuchten Knoten
– Kosten: bei m Operationen höchstens O(mlog*(n))
2
– untere Schranke für amortisierte Kosten: ϴ(m · ⍺(m, n)) mit ⍺ = Inverse der Ackermann-Funktion
– Anwendung: Verwaltung der Zusammenhangskomponenten von Graphen (Kruskal, minimale Spannbäume)
Priority-Queues (Vorrangswarteschlange)
– lineargeordnete Menge
– Operationen: insert, min, access-min, delete-min
– Heap-Bedingung: Knotenmenge V, ∀ x ∈ V ∀ y ∈ V : ist y Nachfolger von x, so ist key(x) ≤ key(y)
– → MinHeap, kleinster Wert in Wurzel
– weitere Operationen: delete, decrease-key (Herabsetzen des Schlüsselwertes eines beliebigen Elementes)
– decrease-key(x, y): key(x) = y
– verschmelzen zweier disjunkter Priority-Queues: union bzw. merge/meld
– Kosten (ähnlich HeapSort):
– Heap-Aufbau: O(n)
– access-min: O(1)
– delete, delete-min, insert: O(log(n))
– decrease-key: O(log(n)) bzw. O(1) für die Wurzel
– merge: O(k·log(n + k)) für k = Knotenanzahl in kleineren Heap und n = Knotenanzahl im größeren Heap
Dijkstra (Einschub)
– Berechnung kürzester Wege in einem gerichteten Graphen, Startknoten s ∈ V, kürzester Weg von s zu allen v ∈ V
– Methode: mit leerer Menge S starten, S schrittweise erweitern bis alle Knoten aus V enthalten → O(nlog(n) + m)
– V \ S als Priority-Queue mit Schlüsselwerten d(v)
– Laufzeit: falls decrease-key in O(1) und access-min, delete-min in O(n), dann Laufzeit von O(n2 + m) = O(n2), falls
m ∈ � (n2 / log(n)) ist diese Datenstruktur mit Heap gleichwertig oder sogar überlegen
Binomial Queues
– Struktur:
– B0: Baum mit einem Knoten
– Bn, n ≥ 1: zwei Binomialbäume Bn-1, wobei die Wurzel eines der Bn-1 in die Wurzel des anderen gehängt wird
– Eigenschaften:
– Bn hat 2n Knoten und die Höhe n → Wurzel von Bn hat Grad n
– die n Teilbäume der Wurzel von Bn sind genau vom Typ Bn-1, …, B1, B0
n
– Bn hat
Knoten der Tiefe i
i
– Bn sollen Heap-Bedingung (Min) genügen, d.h. key(x) ≤ key(y) falls x Vorgänger von y
– Operationen:
– min/access-min: Minimum aller Baumwurzeln, in O(log(N)) da höchstens ⌊log(N) + 1⌋ Bäume
– meld/merge: Baum mit der größeren Wurzel in Baum mit der kleineren hängen, O(1)
– insert: Verschmelzen zweier Wälder Bn und B0: O(1)
– delete-min: Wurzel entfernen → Wald aus Bn-1, Bn-2, …, B1, B0 entsteht, Verschmelzen: O(log(N))
– decrease-key: Vertausche Element mit direktem Vorgänger bis Heap-Bedingung erfüllt, O(log(N))
– delete: Element zum Minimalelement machen, key auf -∞: decrease-key + delete-min, O(log(N))
– Vergleich mit Heap: meld/merge billiger, access-min teurer
– Implementierung:
– pred(x): zeigt auf direkten Vorgänger bzw. auf sich selber falls x Wurzel
– llink(x): zeigt auf linkesten Sohn des Teilbaums mit Wurzel x bzw. auf sich selber falls x Blatt
– rlink(x): zeigt auf rechten Nachbarn/Geschwister (mit gleicher Tiefe relativ zum Unterbaum) bzw. auf seinen
Vorgänger falls x keinen rechten Nachbarn hat

Fibonacci-Heaps
– Liste von heapgeordneten Bäumen, spezieller Zeiger zeigt auf Wurzel mit minimalen Schlüssel
– Operationen:
– init: leerer F-Heap, Zeiger nil
– merge/meld: zwei Listen aneinander hängen, Minimalknoten wird Minimum beider: O(1)
– insert: erzeuge Baum mit einem Knoten, dann merge mit vorhandener Liste von Bäumen (evtl. neues
Minimum): O(1)
– delete-min:
– Baum mit Minimalknoten suchen, sei r Rang dieses Baumes
– Wurzel abtrennen, Baum durch r Teilbäume ersetzen, Marken von neuen Wurzeln entfernen
– Bäume suchen, die den gleichen Grad besitzen und größere Wurzel an kleinere hängen
3
– neuen Minimalknoten bestimmen
– Kosten: O(log(n))
– decrease-key:
– Schlüssel herabsetzen, ggf. Minimalknoten aktualisieren
– ggf. Heap-Bedingung nicht mehr erfüllt → verändertes Element aus Baum löschen und als neuen Baum
einfügen, Vater markieren
– damit Baum durch decrease-key nicht zu sehr in Breite wächst, darf von jedem Knoten nur ein Kind
entfernt werden → Markierungen
– Knoten ist genau dann markiert, wenn er keine Wurzel ist und ein Kind von ihm entfernt wurde
– wird Knoten entfernt, dessen Vater markiert war, so wird Vater mit Kindern selber ein neuer Baum,
Markierung des Vaters wird entfernt und sein (vorheriger) Vater wird markiert (falls dieser selber markiert,
rekursiv fortfahren und ebenfalls als neuen Baum einfügen)
– Kosten: O(k) für k Marken (danach mindestens k – 1 Marken weniger) → O(1)
04 – Algorithmen auf Graphen
Allgemeines
– gerichteter Graph G = (V, E) mit Knotenmenge V und Kantenmenge E ⊆ V⨯V
– Kante e = (v, v') ∈ V mit Anfangsknoten v und Endknoten v'
– inzident: Knoten v, v' mit Kante e und Kante e mit Knoten v, v', adjazent: Knoten v und v'
– Speicherung:
– Adjazensmatrix: bool'sche Matrix mit Speicherbedarf ϴ(|V|2) (unabhängig von Kantenzahl)
– Adjazenslisten: für jeden Knoten Liste mit ausgehenden Kanten, Speicherbedarf: O(|V| + |E|)
– doppelt verkettete Listen: Knoten in Knotenliste, Kanten in Kantenliste
– jeder Knoten in Knotenliste hat drei Zeiger: linker/rechter Nachbar und Kantenliste
– jede Kante in Kantenliste hat drei Zeiger: Vorgänger/Nachfolger in Kantenliste und zugehöriges Element in
Knotenliste
– Eingangs-/Ausgangsgrad: Anzahl der eingehenden/ausgehenden Kanten
– Teilgraph: Knoten-/Kantenmenge sind Teilmengen eines anderen Graphen
– induzierter Teilgraph: alle Knoten der Knotenteilmenge sind mit den gleichen Kanten verbunden wie im Supergraph
– Weg/Pfad: Folge von zusammenhängenden Knoten
– einfacher Weg: kein Knoten wird mehrfach besucht
– Zyklus: Weg von v nach v (keine trivialen Zyklen = Eigenschleifen)
– gerichteter Wald: zyklenfreier gerichteter Graph; gerichteter Baum: gerichteter Wald mit einer Wurzel
Topologische Sortierung
– topologische Sortierung: Knoten so anordbar, dass alle Kanten nur von links nach rechts verlaufen
– → Graph genau dann topologisch sortierbar, wenn er zyklenfrei ist
– Algorithmus:
– alle Elemente ohne Vorgänger entfernen (bilden dann die Anfangselemente der sortierten Liste)
– Vorgängerzahl für jedes übrig gebliebene Element berechnen: Nachfolgerliste durchlaufen, jeweils
Vorgängerzahl um eins erhöhen […]
Transitive Hülle
– gerichteter Graph G* = (V, E*) heißt reflexive, transitive Hülle von G = (V, E)
– gdw. ∀ v, v' ∈ V: (v, v') ∈ E*
– gdw. ∃ Weg von v nach v' in G
– → Kanten von jedem Knoten zu allen indirekt erreichbaren anderen Knoten (Erreichbarkeitsgraph)
– AG Adjazensmatrix von G und In Einheitsmatrix, sei A = AG + In
– berechne A2, A3, …, An mit Ai = Ai-1 · A für i ≥ 2
– bis An ist ausreichend, da Weg von i nach j in G mit Länge ≤ n (falls er existiert)
– A* = AG* = An, Kosten: O(n4)
– Verbesserung: A2 = A2 ⋅A2
, Kosten: O(n3log(n))
– Warshall-Algorithmus (Berechnung der transitiven Hülle): durchlaufen der Adjazensmatrix
– für alle i, k, j von 1...n: wenn Kanten zwischen i und k und Kante zwischen k und j, dann kann i auch j erreichen
– Kosten: O(n3)
– transitive Hülle azyklischer Graphen:
– topologische Sortierung in O(|V| + |E|)
– Berechnung der von i erreichbaren Knoten beginnend mit i = n
– Kosten: O(|V|2 + |E| · |V|)
k
k −1
k −1
4
Kürzeste Wege
– one-to-all (single source shortest paths): Dijkstra in O(m + nlog(n)) unter Verwendung von Fibonacci-Heaps
– alle kürzesten Wege:
– Dijkstra: O(n · m + n2log(n))
– Floyd:
– Prinzip: dynamische Programmierung
– zuerst: Kosten von i nach j mit direkten Kosten initialisieren (bzw. ∞ falls keine entsprechende Kante ex.)
– dann: für alle Zwischenknoten: für alle Startknoten: für alle Zielknoten: Weg über Zwischenknoten besser
als bisheriger Wert, dann diesen dazu nehmen
– aijk = min(aijk-1, aikk-1 + akjk-1) (Länge des Weges von i nach j mit Zwischenknoten höchstens aus 1...k)
– Voraussetzung: keine negativen Kreise (negativen Kanten sind erlaubt)
– Kosten: O(n3) = O(|V|3)
Minimale Spannbäume
– Maggs/Plotkin:
– Voraussetzung: Kosten der Kanten paarweise verschieden
– mit tie-breaking-rule immer erreichbar: neue Kostenfunktion aus lexikografischer Sortierung der alten Kosten
und Knotennummerierung: c'(i, j) = (c(i, j), i, j)
– Lemma: Kante (i, j) ist in MSB gdw. jeder Pfad zwischen i und j, der die Kante (i, j) nicht enthält, mindestens
eine Kante e besitzt mit c(e) > c(i, j)
– w(P) = max{c(vh-1, vh) | 1 ≤ h ≤ l} → Kosten eines Pfades: maximale Kosten einer Kante des Pfades
– w(Pik Pkj) = max(wikk-1, wkjk-1) (nur Wege mit Zwischenknoten aus 1…k)
– wijk = min(wijk-1, max(wikk-1, wkjk-1))
– wijn = min{w(P), P verbindet i und j} (minimales Gewicht aller Pfade zwischen i und j)
– Anfangsbelegung: wij0 = 0 für i=j, c(i, j) falls Kante existiert, ∞ sonst
– Kante (i, j) in MSB gdw. c(j, j) = wijn (ex. also kein günstigerer Pfad zwischen i, j als die Kante zwischen ihnen)
– Kruskal:
– billigste Kante nehmen (globale Suche), die mit den bereits gewählten keinen Kreis bildet
– Union-Find-Struktur zum Speichern der Knoten → gibt an, welche Knoten zusammenhängend sind
– Kosten: O(|E| · log(|V|))
– Prim:
– ähnlich wie Kruskal, startet mit beliebigen Knoten, nimmt billigste Kante hinzu, die einen noch nicht
verbundenen Knoten verbindet (hier keine globale Suche)
– Kreisvermeidung: betrachtet nur Kanten, die von den bisher aufgenommenen Knoten in noch nicht erreichbare
Knoten verlaufen
– Kosten: O(|E| · |V|)
Tiefen- und Breitensuche
– wenn bei einem azyklischen gerichteten Graphen die Knoten bei der Tiefensuche in der Reihenfolge nummeriert
werden, in der sie besucht werden, erhält man eine topologische Sortierung
Steinerbäume
– Knotenmenge V, Terminals N ⊆ V und Steinerknoten V \ N
– Steinerbaum: Teilbaum, der alle Knoten aus N enthält
– Spezialfälle: N = V: Steinerbaum ist Spannbaum; N = {a, b}: Steinerbaum ist Pfad
– Problem: finde Teilbaum, der alle Terminals enthält und minimale Kantengewichte hat, NP-schwer
– vertex cover (Knotenüberdeckung): minimale Menge an Knoten so dass jede Kante zu mindestens einem dieser
Knoten benachbart ist, Problem ist NP-schwer
– Distanzheuristik: Steinerbaum-Problem auf Problem des minimalen Spannbaums zurückführen (Approximation)
– Konstruiere Distanzgraph G' = (N, F): für alle |N| Knoten kürzeste Wege berechnen: |N| Aufrufe von Dijkstra
– Berechne minimalen Spannbaum M' von G'
– Bestimme Teilgraph T' von G durch ersetzen jeder Kante in Spannbaum M' durch zugehörigen kürzesten Pfad
in G
– Berechne minimalen Spannbaum M in von VT' induzierten Teilgraphen von G
– Entferne wiederholt Steinerknoten mit Grad 1 (Blätter) aus M
– Kosten: O(|N| · (nlog(n) + m))
– Güte der Approximation:
– Spannbaum M': c(M') ≤ (2 – 2/|N|) * OPT
– Steinerbaum M: c(M) ≤ c(M')
– Güte liegt also bei ~2
5
05 – Flüsse in Netzwerken
Grundlagen
– Netzwerk N = (V, E, s, t, c) besteht aus
– Graphen G = (V, E), Quelle s ∈ V, Senke t ∈ V
– Kapazitätsfunktion c: V⨯V → N0 mit c(u, v) = 0 für (u, v) ∉ E
– für alle (u, v) ∈ E und alle v ∈ V \ {s, t} gilt: Summe der eingehenden Flüsse = Summe der ausgehenden Flüsse
– Kapazitätsbedingung: f(u, v) ≤ c(u, v) ∀ (u, v) ∈ V⨯V (Fluss kleiner/gleich Kapazität)
– Symmetriebedingung: f(u, v) = -f(v, u) ∀ (u, v) ∈ V⨯V
– Kirchhoffsches Gesetz: ∑ f u , v =0 ∀ u∈V ∖{s , t }
v∈V
– Wert eines Flusses f:
∣ f ∣= ∑ f s , v (alle von s ausgehenden Flüsse)
v∈V
– Rest-/Residualnetzwerk Nf = (V, Ef, s, t, cf) mit Restkapazität cf(u, v) = c(u, v) – f(u, v) und Ef = {(u, v) | cf(u, v) > 0 }
– Erweiterungspfad (augmenting path): Pfad p in Nf von s nach t, bei dem für alle Kanten gilt: cf(u, v) > 0,
Restkapazität cf von p ist minimale Kapazität der Kanten
Algorithmus von Ford und Fulkerson
– Idee:
– solange es Weg von s nach t in Nf gibt, bestimme Erweiterungspfad p in Nf
– berechne zulässigen Fluss g für p in Nf mit |g| = cf(p)
– für alle Kanten in g Fluss entsprechend erhöhen und Fluss der Rückwärtskanten setzen
– erzeugt bei Terminierung (s, t) – Schnitt: die maximale von s im Residualnetzwerk erreichbare Knotenmenge
– Kosten: O(|f*| · |E|), kann bei ungünstiger Flusswahl exponentielle Laufzeit benötigen
Schnitte und Flüsse
– Schnitt: Zerlegung der Knotenmenge V = A ∪ B, A ∩ B = Ø, s ∈ A, t ∈ B
– Kapazität eines Schnittes (A, B): c A , B = ∑ cu , v  (Summe Kapazitäten der durchtrennten Kanten)
– Fluss über Schnitt (A, B):
f  A , B=
∑
u∈ A , v∈ B
f u , v
u ∈A , v ∈B
– für jeden zulässigen Fluss f und jeden beliebigen (s, t) – Schnitt (A, B) gilt: f(A, B) ≤ c(A, B)
– für jeden beliebigen (s, t) – Schnitt (A, B) gilt: |f| = f(A, B)
– Min-Cut-Max-Flow-Theorem: Sei f zulässiger Fluss in Netzwerk N mit Kapazität c, dann sind folgende Aussagen
äquivalent:
– f ist maximaler Fluss in N
– Restnetzwerk Nf enthält keinen Erweiterungspfad
– |f| = c(A, B) für einen (s, t) – Schnitt (A, B)
– → maximaler Fluss = minimaler Schnitt
Algorithmus von Edmonds-Karp
– verwendet jeweils kürzesten Erweiterungspfad → stellt sicher, dass Algorithmus in polynomieller Zeit terminiert
– kürzester Erweiterungspfad beispielsweise durch Tiefensuche ermittelbar
– Kosten: O(|V| · |E|) Suchen nach Erweiterungspfad, O(|V| · |E|2) Schritte
Zuordnungsprobleme (Matching)
– Graph G = (V, E), Matching M ist Teilmenge M ⊆ E so dass für jeden Knoten v ∈ V höchstens eine inzidente Kante
in M ist (keine zwei Kanten besitzen einen gemeinsamen Knoten)
– maximales Matching M ist ein Matching mit maximaler Anzahl von Kanten
– bipartiter Graph: V kann in zwei Knotenmengen L und R zerlegt werden mit V = L ∪ R, L ∩ B = Ø, für jede Kante
(u, v) ∈ E gilt: u ∈ L und v ∈ R (bipartites Matching: Matching in einem bipartiten Graphen)
– Lemma: wenn G bipartit und M Matching in G, dann ex. zulässiger Fluss fM in NG mit ganzzahligen
Kantenflusswerten und |fM| = |M|
– Lemma: G = (L ∪ R) bipartit, f ganzzahliger Fluss in NG, dann gibt es Matching Mf in G mit |Mf| = |f|
– Satz: sei G = (L ∪ R) bipartit, NG zugehöriges Netzwerk, dann ist Mf = {(l, r) | f(l, r) = 1} maximales Matching gdw.
f ist maximaler Fluss in NG
6
Herunterladen