Algorithmen 17. Juli 2007 Die Vorlesung wurde gehalten im S OMMERSEMESTER 2007 von N INA Z WEIG und P ROF. D R . M ICHAEL K AUFMANN Diese Vorlesungsmitschrift wurde angefertigt von: T ILL H ELGE H ELWIG Lektorat: Nina Zweig Diese Mitschrift erhebt keinen Anspruch auf Vollständigkeit. Ich bemühe mich zwar darum, aber im Endeffekt sind es meine persönlichen Notizen aus der Vorlesung und kein offizielles Skript. Verbesserungsvorschläge nehme ich gerne entgegen und freue mich natürlich auch, wenn jemand die Mitschrift gebrauchen kann und als Gegenleistung vielleicht korrekturliest. 1 Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 1 3 ANALYSE VON ALGORITHMEN Worum geht es? 1. Algorithmendesign (a) (b) (c) (d) von der alltagssprachlichen Fragestellung zum mathematisch formalen Problem Formulierung des Lösungswegs Korrektheit Analyse i. des Laufzeitverhaltens und ii. des Speicherbedarfs 2. Implementierung (a) Datenstrukturen (b) Java, OOP 3. Experimentelle Analyse als Teil des “Algorithm Engineering” 2 Was ist ein Algorithmus? Nach Donald E. Knuth: Instanz: konkrete Eingabe I ∈ D für einen Algorithmus 3 Analyse von Algorithmen • Laufzeit • Speicherverbrauch Sei A ein Algorithmus mit D und B und I ∈ D eine Instanz, dann kann die Laufzeit TA (I) berechnet bzw. gemessen werden. 3.1 Allgemeine Komplexität 1. worst-case-Komplexität: TA,wc (n) = max {TA (I)| |I| = n} I∈D Was ist “n”? Charakteristische Größe einer Eingabe, z.B. Anzahl der Elemente einer Menge, Größe eines Arrays, etc. 2. average-case-Komplexität: P 1 TA,avg (n) = |{I||I|=n TA (I) }| I=D |I|=n 2 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 3.2 3 ANALYSE VON ALGORITHMEN Asymptotische Notation • Implementierung • Compiler/Interpreter • Maschinenbefehlssatz Asymptotische Notation als Betachtung des Funktionsverhaltens für große n. Dazu: Unterteilung des Algorithmus in elementare Operationen: solche mit “konstanter” Laufzeit, z.B. Zuweisungen, kleinere Additionen, Inkrementieren, ... Diese haben die Laufzeit O(1). 3.3 O-Notation “Groß-O” von: O(f (n)) :=Menge von Funktionen g(n), die sich asymptotisch “gleich” verhalten, im Sinne von: ∃c > 0, ∃n0 , ∀n ≥ n0 : g(n) ≤ c · f (n) Beispiel: g(n) = 5n2 + 13n + 5 g(n) ∈ O(n2 )? Ja: f (n) = n2 , ∃c : c = 13 ⇒ n ≥ 3 ⇒ g(n) ≤ 13n2 Anschaulich: O-Notation beschreibt “obere Schranke”. 3.4 O-Notation “klein-o” von: O(n) ist eine Menge von Funktionen g(n) mit: ∀c> 0, ∃n0 > 0, ∀n ≥ n0 : g(n) ≤ c · f (n) Beispiel: Ist g(n) ∈O(n2 )? Nein: c = 1 @n0 , so dass ∀n ≥ n0 : g(n) ≤ c · f (n) aber: g(n) ∈O(n3 ) Anschaulich: O-Notation beschreibt die “echt größere Schranke”. 3.5 Ω-Notation “Groß-Omega”: ∃c > 0, ∃n0 > 0, ∀n ≥ n0 : g(n) ≥ c · f (n) “untere Schranke” 3.6 ω-Notation “klein-omega”: ∀c > 0, ∃n0 > 0, ∀n ≥ n0 : g(n) ≥ c · f (n) “echt kleinere untere Schranke” 3.7 Θ-Notation “Theta”: Schnittmenge Θ(f (n)) = O(f (n) ∩ Ω(f (n)) 3 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 4 SUCHEN Beispiel: g(n) ∈ O(f (n)) g(n) ∈ Θ(f 0 (n)) ⇔ g(n) ∈ O(f 0 (n)) ∧ g(n) ∈ Ω(f 0 (n)) g(n) ∈ ω(f 00 (n)) Anmerkung: Schreibweise oft: g(n) = O(n2 ) Zurückkommend auf die Notation: O(1)=konstante ˆ Laufzeit bedeutet, dass wir die genaue Anzahl von Rechenschritten nicht kennen (müssen), solange wir annnehmen können, dass sie auf jedem Systen in “kleiner”, von der Größe des Operanden unabhängige Anzahl von Schritten berechnet werden. Beispiel: Gleichheit von zwei Objekten ⇒ Zeigervergleich ⇒ elementare Operation Schwierig: Addition von Integern ⇒ bis zu einer natürlichen Grenze elementar ⇒ danach nicht mehr 4 Suchen Eingabe: Datenstruktur d mit Menge von Objekten O, deren Menge von Schlüsseln S und ein Schlüssel h ∈ U. Ausgabe: Objekt o ∈ d mit o.key == k, wenn o ∈ / d Rückgabe ’null’. Konvention: d.get(i) gibt i-tes Element aus d zurück (auch für Arrays - normal A[i]). 4 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 4 SUCHEN Annahme: Elemente der Menge S im Rechner darstellbar ⇒∃f : S ⇒ N, R. z.B. Universum U im Folgenden N. Wir benutzen Datenstruktur d mit folgenden Operationen: • Initialisieren • Einfügen • Suchen Einfache Datenstrukturen: Liste, Array 4.1 Lineare Suche Sei U = {1, . . . , n}, gesucht ist k ∈ U . Object o = null; for Object object in d do if object.key == k then o = object; break; return o; Zeile 2 in Java 1.4 (Objekte mit Interface List): for (Iterator it = d.getIterator(); it.hasNext(); ) { Object object = it.next(); ... } Finite Berechnung: √ Korrektheit: Schleifeninvariante im k-ten Durchlauf: o zeigt auf null, wenn Objekt bisher nicht gefunden, sonst auf Objekt. Laufzeitanalyse: worst case Zeilen 1,3-6: konstant Zeile 2: O(n) ⇒ Gesamtlaufzeit: O(n) Laufzeitanalyse: average case Annahme, dass k ∈ S gleichverteilt ⇒jeder Schlüssel hat gleiche Wahrscheinlichkeit, gesucht zu werden. n P Tlin.Such, avg (n) = n1 i = n(n+1) 2 i=1 Typische Falle beim Programmieren for (int i = 0; i < d.size(); ++i) Object object = d.get(i); Wenn d ist Liste, d.get(i) = O(n) ⇒ worst-case-Laufzeit: O(n2 ) 5 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 4.2 4 SUCHEN Binäre Suche Eingabe: Datenstruktur d enthält Menge von Objekten O mit Schlüsselmenge S, k ∈ S. Zusicherung: Elemente in d nach ihrem Schlüssel (aufsteigend) sortiert. Ausgabe: siehe lineare Suche Idee: Größe der Datenstruktur (des Suchraumes) in jedem Schritt halbieren. 1 Object binarySearch(DataStructure d, int k) 2 if (d.size() == 1) return (d.get(0).key==k ? d.get(0) : null 3 | {z } ); | {z } | {z } false true Bedingung 4 if (d.size() == 0) 5 return null; 6 int probeIndex = d.size() / 2; 7 Object probe = d.get(probeIndex); 8 if (probe.key == k) 9 return probe; 10 else 11 return (probe.key > k ? // k liegt “vor” probe 12 binarySearch(d.firstHalf(), k) : 13 binarySearch(d.secondHalf(), k)); Finitheit:√ Datenstrukturgröße wird in jedem Schritt halbiert. Für d.size() ∈ {0, 1} wird Rückgabe erzwungen. Korrektheit: Durch Sortierung kann k nur in der beibehaltenen Hälfte liegen. Laufzeitanalyse: Zeilen 1-6: O(1) Zeile 7: O(1) für Array O(n) für Liste Zeilen 11-13: “Durschschneiden” O(n) für Liste (zum Element laufen, durchschneiden) O(n) für Array (Kopieren) ⇒ log n rekursive Aufrufe ⇒ jeder Aufruf hat O(n) ⇒ O(n log n) Verbesserung: Suchraum durch zwei zusätzliche Variablen low , high einschränken. 6 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 4 SUCHEN Object binarySearch(DataStructure d, int k, int l, int h) // Annahme: Beim ersten Aufruf l=0, h=d.size()-1 if ((h-l) == 1) return (d.get(0).key == k ? d.get(0) : null); if ((h-l) == 0) return null; int probeIndex = l + (h-l+1) / 2; Object probe = d.get(probeIndex); if (probe.key == k) return probe; else return (probe.key > k ? binarySearch(d, k, l, probeIndex - 1) : binarySearch(d, k, probeIndex + 1, h); Laufzeitanalyse: Zeilen 1-3,5-7: O(1) Zeile 4: O(1) für Array O(n) für Liste Zeile 8: O(1) ⇒ Gesamtlaufzeit: O(n log n) für Liste, O(log n) für Array Alternative: Laufzeitals Rekursion n , T (1) = T (0) = c2 T (n) = c1 +T |{z} | {z2 } ≈Zeilen 1-7 Zeile 8 Geratene geschlossene Form: T (n) = c1 · log n + c2 Induktionsbeweis: zu zeigen: T (n) = c1 + T ( n2 ) mit T (1) = c2 ⇔T (n) = c1 · log n + c2 √ Induktionsanfang: T (1) = c2 = c1 · 0 + c2 Induktionsannahme: Sei T (k) = c1 · log k + c2 ∀k ≤ n, k, n ∈ N Induktionsschritt: Sei n0 = 2k für irgendein k ≤ n. T (n0 ) n0 ) Rekursionsgleichung 2 = c1 + T (k) = c1 + c1 · log k + c2 Induktionsannahme = c1 + T ( = c1 (log(2k)) + c2 = c1 · log n + c2 7 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 4 SUCHEN Laufzeit: average case Im Allgemeinen: In Schritt i können genau 2i−1 Elemente gefunden werden. Annahme: n = 2p − 1, p ∈ N p p p P P P 1 1 i · 2i = 2n i · 2i Tbin.Such,avg (n) = n1 i · 2i−1 = 2n i=1 i=1 i=0 p P 1 Sp := 2n i · 2i i=0 p+1 Sp + (p + 1) · 2 = p P i+1 (i + 1) · 2 i=0 = p X i+1 i·2 i=0 | + p X 2i+1 i=0 {z 2·Sp } | {z } 2p+2 −2 ⇒ Sp = 2p+1 (p − 1) + 2 Zurück zu T : Tbin.Such,avg (n) = ≈ = 1 (p − 1) · 2p+1 + 2 2n 1 (n · log n − n) n log n − 1 n = 2p − 1, p ≈ log n Im Durchschnitt nur einen Aufruf weniger als im schlechtesten Fall. 4.3 Interpolationssuche Motivation: Sie suchen “Laubfrosch” und schlagen den Duden bei “Paradoxon” auf Seite 800 auf. L = 12 . . . Buchstabe P = 16 . . . Buchstabe Sie suchen weiter auf Seite 12 16 · 800 = 600 Allgemein: jLineare Interpolationssuche k · (k − l + 1) + l probeIndex = k−d.get(l).key ∆ k . . . gesuchter Schlüssel l . . . ”lower bound” h . . . “higher bound” ∆ . . . noch “offener” Suchraum der Schlüssel ∆ : g.get(h).key − d.get(l).key Beispiel: j 70−50 150−50 k · (22 − 3 + 1) + 3 = 8 Laufzeitanalyse unter der Annahme der Gleichverteilung: Wir zeigen, dass TInterpol’,avg = O(log log n). Interpol’: Leichte Variante der Interpolationssuche. Idee: Suche Objekt an der Stelle probeIndex. 8 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 4 SUCHEN Suche sukzessive an probeIndex + i · probeIndex − i · √ √ n , wenn k größer und n , wenn k kleiner Frage: Wie oft müssen wir i inkrementieren (dekrementieren) bis der Suchraum auf √ ⇒ Mindestens zwei Vergleiche an Stelle probeIndex, probeIndex + n. √ neingegrenzt ist. Wie hoch ist der Erwartungswert für die Anzahl C von nötigen Vergleichen? C = X i · P r [genau i Vergleiche nötig] i≥1 = X P r [mindestens i Vergleiche nötig] i≥1 Würfel: 1 1 1 1 1 1 6 · 1 + 6 · 2 + 6 · 3 + 6 · 4 + 6 · 5 + 6 · 6 = 3, 5 5 4 3 2 1 1 + 6 + 6 + 6 + 6 + 6 . . . Wahrscheinlichkeit, dass mindestens eines 1 gewürfelt wird. Wann sind mehr als 2 Vergleiche nötig? √ Wenn die tatsächliche Position x von k von der erwarteten Position µ := probeIndex um mehr als n abweicht. Wo liegt µ? Gleichverteilung ⇒ Jeder Schlüssel innerhalb von ∆ hat dieselbe Wahrscheinlichkeit p = k−d.get(l).key , dass er kleiner ist ∆ als k. Damit die Wahrscheinlichkeit, dass von h − l + 1 = n Elementen, die wir aus ∆ ziehen, genau j kleiner ist n sind, pj (1 − p)n−j . j Somit n X n µ = j pj (1 − p)n−j = pn j j=1 σ 2 = p(1 − p)n Die Wahrscheinlichkeit, dass der Wert einer Zufallsvariablen um mehr als t von µ abweicht: σ2 Chebyshev t2 √ p(1 − p)n t := (j − 2) · n ≤ (j − 2)2 n √ in unserem Fall: P r [|x − pn|] ≥ (j − 2) · n P r [|x − µ| ≥ t] ≤ P1 1 Wegen p(1 − p) ≤ 14 ⇒ C ≤ 2 + 4 (i−2)2 = 2, 4 i≥3 √ T (n) = C + T ( n) T (1) = O(1) T (n) ≤ 2, 4 log log n 9 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 4 SUCHEN Rekursionen Beispiel: T (n) i-ter Schritt nach x Schritten √ n +c q √ n +c+c = T 1 = T n 2 + 2c = T .. . 1 = T n 2i + i · c 1 = T n 2i+1 + (i + 1) · c .. . = T (2) +x · c | {z } =O(1) 1 n 2x 1 ⇔ log n 2x log n T (n) = T (n) = = 2 1 = · logn = 1 2x = 2x ⇒ log log n = x (x + 1) · c = 2, 4 log log n 9 n +n 5·T 10 = O(nk ) k konst. Master-Theorem Löse T (n) = a · T n b + f (n) mit a, b ≥ 1 und f (n) > 0. Satz: Sei a ≥ 1, b > 1 konstant und f (n), T (n) nicht negativ mit T (n) = a · T bzw. nb steht). n b + f (n) (wobei n b für n b 1. Für f (n) = O n(logb a)− , > 0 ist T (n) = Θ nlogb a . 2. Für f (n) = Θ nlogb a ist T (n) = Θ nlogb a · logb n . 3. Für f (n) = Ω nlogb a+ , > 0 und a · f nb ≤ c · f (n) für c < 1 ⇒ T (n) = Θ(f (n)). Beispiele: • T (n) = 9 · T n3 + n a = 9, b = 3 f (n) = nlog3 9−1 = n2−1 = n ⇒ Fall 1: T (n) = O nlog3 9 = O n2 • T (n) = T n2 + 1 (binäre Suche) a = 1, b = 2 f (n) = 1 = nlog2 1 = n0 = 1 ⇒ Fall 2: T (n) = O (1 · log2 n) = O(log n) 10 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 4 SUCHEN • T (n) = 2 · T n2 + n log n (“divide and conquer”) a=2=b f (n) = nlog2 2− ? Gehtnicht! Satz nicht anwendbar. Fall 3 fordert Ω n1+ , was durch n log n nicht erfüllt ist. Beweis: Annahme: n = bi , i ∈ N Teilprobleme haben Größe 1, b, b2 , . . . , bi Lemma: Sei a > 1, b > 1, f (n) nicht negativ, n = bi ( O(1) für n = 1 T (n) = a · T nb + f (n) für n = b dann gilt: logb n−1 T (n) = Θ nlogb a + X j=0 aj · f n bj Beweis: T (n) n = f (n) + a · T nb n = f (n) + a f +a·T 2 b n nb 2 +a ·T 2 = f (n) + a · f b n n bn 2 = f (n) + a · f + a · f 2 + a3 · T 3 b b b .. . logj n−1 n X = aj · f j + Θ nlogb a b j=0 = alogb a · O(1) + logP b n−1 = nlogb a · O(1) + logP b n−1 aj · f n bj j=0 aj · f n bj j=0 Lemma: Sei a ≥ 1, b > 1, f (n) definiert auf bi , i ≥ 0. Sei g(n) definiert als g(n) = logP b n−1 j=0 aj · f n bj . 1. Ist f (n) = O n(logb a)− , so ist g(n) = O nlogb a Beweis: 11 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) Mit f (n) = \logb a− ist f n bj = logb a− \ b| g(n) 4 . logb a− logb n−1 X = O SUCHEN aj · n = O nlogb a− · bj j=0 logb n−1 X j=0 logb n−1 X = O nlogb a− a · b blogb a j j (b ) j=0 = = ≤ = ·logb n −1 O n b − 1 logb a− n − 1 O n · b −1 logb a− O n · n O nlogb a logb a− b a 2. Mit f (n) = Θ nlogb gilt g(n) = Θ nlogb a · logb n . Beweis: logb a Mit f (n) = Θ nlogb a ist f bnj = Θ bnj . g(n) = Θ X aj · n logb a j=0 bj = = = j 1 logb a b j=0 j X 1 Θ nlogb a · aj · a j=0 logb n−1 X Θ nlogb a · 1 Θ nlogb a · X aj · j=0 = Θ n logb a · logb n n 3. Ist a · f b ≤ c · f (n), c < 1, n > b, so ist g(n) = Θ (f (n)). Beweis: P Da g(n) = aj · f bnj ist g(n) ≥ f (n). j=0 Noch zu zeigen: g(n) = O(f (n)) Wegen a · f nb ≤ c · f (n) für c < 1 gilt aj · f bnj ≤ cj · f (n) logP b a−1 P j loga n−1 c · f (n) = f (n) · cj = f (n) · c c−1 −1 ≤ O(f (n)) ⇒ g(n) ≤ j=0 j=0 Es gilt alogb n = nlogb a , denn nlogb a = blogb n·logb a = alogb n . 12 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 5 5 SORTIEREN Sortieren Eingabe: Menge O von Objekten, auf denen eine Ordnung definiert ist, so dass für je zwei Objekte o1 , o2 ∈ O jeweils entweder a) o1 < o2 b) o1 == o2 c) o1 > o2 gilt. Ausgabe: Die selbe Menge sortiert, so dass für alle Objekte oi (:= oi steht an Position i) und oj mit 0 ≤ i < j < |O| gilt oi ≤ oj . Im Folgenden gehen wir von zu sortierendem Array S mit natürlichen Zahlen aus. 5.1 Sortieren durch Einfügen Idee: Zweites S 0 mit der gleichen Länge n := |S|. Suche in Runde i ≥ 0 die kleinste Zahl aus S, streiche sie in S, füge sie an Position i in S 0 ein. Streichen implementieren • durch eine negative Zahl (falls Eingabe nur positiv) • durch null-Objekt • durch zusätzliches boolean Array Finitheit √ , Korrektheit √ Laufzeitanalyse: n Runden mit jeweils O(n) Laufzeit, n−1 P n−i= i=0 n·(n−1) 2 Schlechte Laufzeit, weil wir jedes Element aus S mit jedem anderen vergleichen. Idee: Wähle sogenanntes Pivotelement p und bilde zwei Mengen K = {x ∈ S \ p|x ≤ p} G = {x ∈ S \ p|x > p} Wenn K und G sortiert sind, können wir S wie folgt zusammensetzen: S = K[0] . . . K[|K| − 1]pG[0] . . . G[|G| − 1] Sei k := |K| und g := |G| ⇒ Position von p im sortierten Array ist S[k]. ⇒ Verfahren rekursiv auf die Teilmengen K und G anwenden. Wir führen Variablen lef tIndex, rightIndex ein, um zu sortierende Teilbereiche zu kennzeichnen. Es ist klar, dass alle Elemente aus K kleiner sind als alle Elemente aus G, d.h. wir sparen k · g Vergleiche. 5.2 QuickSort Grobe Idee: Quicksort (int[] s, int leftIndex, int rightIndex) if (leftIndex < rightIndex) int indexOfPivotElement = partition(s, leftIndex, rightIndex); Quicksort(s, leftIndex, indexOfPivotElement - 1); Quicksort(s, indexOfPivotElement + 1, rightIndex); 13 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 5 SORTIEREN Was leistet Funktion partition? • Wählt ein Pivotelement p aus. Hier p := S[lef tIndex] • Setzt alle Elemente kleiner oder gleich p in linke “Hälfte”, größere in rechte “Hälfte” und p auf die Position dazwischen. • Diese Position wird zurückgegeben. Idee: Wir benutzen zwei Zeiger i := lef tIndex + 1 j := rightIndex Das Verfahren teilt das Problem in Teilprobleme, “bezwingt” diese durch rekursive Anwendung und kombiniert diese Teillösungen dann zur Gesamtlösung. ⇒ Divide and Conquer Verfahren Analyse: Allgemeine Rekursionsformel: T (n) = Θ(n) + T (|K|) + T (|G|) + O(1) | {z } | {z } | {z } Divide Conquer Combine Worst-case Analyse: Pivotelement ist Minimum bzw. Maximum ⇒ entweder |K| = 0 oder |G| = 0 ⇒ T (n) = Θ(n) + T (n − 1) + O(1) ⇒ O(n2 ) ⇒ genauso schlecht wie Sortieren durch Einfügen Average-case Analyse: Annahme: Elemente sind paarweise verschieden. In jedem Teilproblem wählen wir zufällig ein Element aus allen als Pivotelement. ⇒ Die Wahrscheinlichkeit, das Element zu ziehen, das in der sortierten Folge an Position j steht, ist n1 für alle Elemente des Teilproblems. Sei QS(n) die erwartete Anzahl von Vergleichen im Feld der Größe n. Klar: QS(0) = QS(1) = 0 Für n ≥ 2: QS(n) = |{z} n +E(QS(j − 1) + QS(n − j)) Divide Wenn das j-te Element der sortierten Folge als Pivotelement gewählt wird. Wegen Linearität des Erwartungswertes: n n−1 P P QS(n) = n + n1 · (QS(j − 1) + QS(n − j)) = n + n2 QS(j) j=1 j=1 14 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) Trick: n · QS(n) = n2 + 2 · 5 n−1 X SORTIEREN (1) QS(j) j=0 (n + 1) · QS(n + 1) = (n + 1)2 + 2 · n X (2) QS(j) j=0 2-1: (n + 1) · QS(n + 1) − n · QS(n) = (n + 1)2 − n2 + 2 · QS(n) ⇔ (n + 1) · QS(n + 1) = n2 + 2n + 1 − n2 + QS(n) · (2 + n) n+2 ⇒ QS(n + 1) ≤ 2 + · QS(n) n+1 n+1 n+2 (2 + · QS(n − 1)) ⇒ 2+ n+1 n 2 2 2 = 2 + (n + 2) · + + + ... n+1 n n−1 1 1 1 + + + ... = 2 + 2 · (n + 2) · n+1 n n−1 | {z } harmonische Reihe n+1 X1 = 2 + 2 · (n + 2) · i i=1 ≤ 2 + 2 · (n + 2) · (ln n + 1) = O(n log n) Anmerkung: Annahme kann erzwungen werden durch • Shuffling • Wählen des Pivotelements “uniformly at random” 5.3 Idee: MergeSort Teile zu sortierende Menge in zwei gleichgroße Hälften und sortiere diese (Divide & Conquer). Kombiniere: n Allgemeine Rekursion: T (n) = O(n) + 2 · T ( ) + Θ(n) | {z } | {z 2 } | {z } Divide Conquer Combine Rückwärts-Analyse MergeSort hat die Laufzeit O(n log n) 15 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 5 SORTIEREN Variante: m-Wege-Mischen Füge immer m sortierte Teilmengen zusammen. ⇒ Θ(n logm n) 5.4 Heap: HeapSort Ist ein binärer Baum mit Wurzel r. Formal ist ein Baum ein Graph, als solcher ist er ein Paar T = (V, E) V . . . Menge der Knoten E ⊆ V × V . . . Menge der Kanten (Relation) Kanten e = (v, w) sind gerichtet. v ist Vater von w. w ist Kind von v. Was ist ein Baum? Ein zykelfreier Graph. (Hierfür betrachten wir Kanten als ungerichtet.) Ein Zykel ist ein Pfad, der mindestens einen Knoten mehrfach durchläuft. Ein Pfad ist eine Folge von Kanten P (v, w) = {(v, v1 ), . . . , (vk−1 , w)}, so dass (v, vi ) ∈ E, (vk−1 , w) ∈ E und ∀ 1 ≤ i < k − 1 : (vi , vi+1 ) ∈ E. Binärer Baum: • Jeder Knoten außer der Wurzel hat einen Vater • Jeder Vater hat höchstens zwei Kinder Idee beim HeapSort: Sei S eine Menge zu sortierender Zahlen. Die Knoten speichern die Zahlen. Baue Heap so auf, dass für alle Knoten gilt, dass die Kinder kleinere Werte haben. ⇒ Heapeigenschaft Wenn die Teilbäume gleich groß sind, muss man nur log n Vergleiche machen, bis man den richtigen Vater gefunden hat. Beispiel: HeapSort 16 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 5 SORTIEREN • Sortieren durch Minimum-Suche • Minimum steht in Wurzel ⇒ Streichen und Heap neu aufbauen (2) • Füge letztes Element im Baum an Stelle der Wurzel ein (3) • Stelle Heapeigenschaft wieder her: • Lasse 9 im Baum runtersinken (4) • Falls 9 ≥ min(key(lchild), key(rchild)), tausche 9 mit min(lsohn, rsohn) per Iteration (5) ⇒ Heapeigenschaft erhalten √ • Iteration mit Minimumsuche! Korrektheit: 1. Im Heap steht in jeder Interation das minimale Element oben. Dieses wird jeweils herausgeschrieben. 2. Durch Runtersinkenlassen wird Heapeigenschaft wiederhergestellt. Laufzeit: O(n · T (Wiederherstellen der Heapeigenschaft)) Ziel: O(log n) für Wiederherstellen der Heapeigenschaft 1. Vergleiche des Schlüssels mit Schlüssel der Kinder und anschließender Tausch möglichst in O(1). 2. Schätze die Zahl der Iterationen beim Runtersinken mit dem Ziel O(log n). Benutze ausgewogene Bäume: Es gibt k ≥ 0, so dass alle Blätter die Tiefe k oder k + 1 haben und Blätter auf Stufe k + 1 sitzen “ganz links”. Ausgewogene Bäume haben eine kurze und einfache Darstellung als Array mit folgenden Eigenschaften: Für Knoten mit Index i hat parent(v) den Index 2i , lchild(v) den Index 2i und rchild(v) den Index 2i + 1. Tiefe: log n 17 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 5 SORTIEREN Baum existiert nur implizit und wird explizit als Array dargestellt: 1. geht in O(1) und 2.? Es fehlt: Initialisierung des Arrays: for all x ∈ S do Insert(x, h) Insert(x, h) Sei n die Größe von h n←n+1 A(n)←x finish ← (i=1) while not finish do j← 2j if A(j)<A(i) then tausche(A(i), A(j)) i←j else finish←true od Laufzeit: • Initialisierung: O(n · Höhe(h)) • n · T (Minimale Suche und Wiederherstellung) = O(n · Höhe(h)) Lemma: Hat ein ausgewogener Baum die Höhe k, so hat er mindestens 2k Knoten. Beweis: per Induktion über k ⇒ Ein ausgewogener Baum mit n Knoten hat eine Höhe kleiner als log n. Satz: HeapSort geht in O(n log n) 5.5 BucketSort Gegeben seien Wörter über einem Alphabet Σ und |Σ| = m. Sortiere lexikographisch: Ordnung: a < b, a < aa < ab Fall 1: Wörter haben alle Länge 1. Wir stellen m Fächer zur Verfügung (für a, b, c, ..., z) und werfen die Wörter in die entsprechenden Fächer. Anschließend werden die Fächer konkateniert. Sei n = #Wörter, k die Länge (k = 1), dann ist die Laufzeit O(n + m). Implementierung: Einzelne Fächer als lineare Listen und diese zusammen als Array. 18 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 5 SORTIEREN Fall 2: Alle Wörter haben Länge k. Sei ein Wort ai = ai1 ai2 . . . aik . Idee: Sortiere zuerst nach dem letzten Zeichen, dann nach dem vorletzten usw. Damit sind Elemente, die zum Schluss ins gleiche Fach fallen, automatisch geordnet. ⇒ O((n + m)k) Beispiel: 124, 223, 324, 321, 123 k=3 Fächer: 1 1 3211 2 3 1231 1242 2 3211 2232 1233 1244 3245 2233 3 2232 1233 4 1244 3245 3214 3245 [Indizes zeigen die Reihenfolge des Aufsammelns.] Problem: Aufsammeln der Listen. ⇒ Überspringe leere Listen mit dem Ziel, eine Laufzeit von O(nk + m) statt O(nk + mk) zu erreichen. Trick: Erzeuge Paare (j, xij ) mit 1 ≤ i ≤ n und 1 ≤ j ≤ k. Sortiere nach der zweiten Komponente und dann nach der ersten. Dann liegen im j-ten Fach die Zeichen sortiert vor, die an der j-ten Stelle vorkommen. Beispiel: Bilde (j, xj ) mit 1 ≤ j ≤ 3 j = 3: (3, 1), (3, 3), (3, 3), (3, 4), (3, 4) j = 2 : (2, 2), (2, 2), (2, 2), (2, 2), (2, 2) 1 1 1 2 3 3 2 2 2 2 2 2 3 ← 1. Komponente 1 ← springe zu Fach 3 3 3 ← springe zu Fach 4 4 4 ↑ 2. Komponente Dadurch wird Durchlaufen leerer Fächer vermieden. Nach dem Preprocessing sortiere wie oben mit dem Wissen, welche Fächer in der j-ten Iteration (1 ≤ j ≤ k) leer bzw. nicht leer sind. ⇒ O(nk + m) (Jedes Element wird k-mal angeschaut) Fall 3: Wir nehmen an ai hat Länge li und L = n P li mit dem Ziel O(L + m) i=1 Idee: Sortiere Wörter der Länge nach. Beziehe zuerst die langen Wörter in den Algorithmus ein. 1. Erzeuge Paare (li , ai ). 19 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 5 SORTIEREN 2. Sortiere Paare durch BucketSort nach ihrer ersten Komponente. Sei L(k) die Liste der Wörter der Länge k. 3. Erzeuge L Paare (j, xij ) mit 1 ≤ i ≤ n und 1 ≤ j ≤ li . Sortiere zuerst nach der zweiten Komponente und dann nach der ersten. Damit erhalten wir lineare Listen, die nicht lineare Fächer angeben für die Iterationen 1 ≤ j ≤ lmax . 4. Sortiere ai durch BucketSort wie im Fall 2. Betrachte nur L(k)-Listen. Satz: BucketSort sortiert n Wörter der Gesamtlänge L über dem Alphabet Σ mit |Σ| = m mit der Laufzeit O(L + m). 5.6 Auswahlproblem Finde Median. Gegeben ist eine Menge S von Zahlen und eine Zahl 1 ≤ i ≤ n. Finde ein x ∈ S mit |{y|y > x}| = i. 1. Idee: Sortiere und laufe dann die Menge ab bis zur i-ten Stelle. → O(n log n) 2. Idee: Ähnlich wie QuickSort. Bei perfekter Teilung erhält man n + Also läuft es im Mittel in linearer Zeit, wenn p zufällig ist. n 2 + n 4 + n 8 + · · · ≤ 2n Deterministisch: |S| = n 1. Wir teilen n Elemente in 5-er Gruppen auf, erhalten also Gruppe. n 5 Gruppen. Bestimme den Median in jeder 2. Suche rekursiv den Median x der Mediane. 3. Teile die Menge S bezüglich x in Mengen S1 , S2 auf. Sei |S1 ∪ x| = k, |S2 | = n − k. 4. Algorithmus: if i=k then return x else if i<k then Auswahl(S1 , i) else Auswahl(S2 , i-k) Wieso betrachten wir Mediane? 1 10 n 1 der Mediane sind größer als x und 10 n der Mediane sind kleiner als x. Die betreffenden Gruppen 3 liefern mindestens 3 Elemente, die größer bzw. kleiner sind als x. Also hat S1 mindestens 10 n Element, 7 genauso S2 . Außerdem ist |S1 | ≤ 10 n, genauso |S2 |. 7 ⇒ T (n) = T ( n5 ) + T ( 10 n) + O(n) für n ≥ d konstant T (n) = O(1) für n ≤ d. Behauptung: T (n) ≤ c · n für c konstant n 7 +c· n+a·n 5 10 9 ≤ c· n+a·n 10 c ≤ c·n für a ≤ 10 T (n) ≤ c · 20 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 6 BALANCIERTE BÄUME public Object search (int key, Tree tree) Node currentEl = tree.getRoot(); boolean found = false; int currentKey; while (currentEl != null && !found) { currentKey = currentEl.getObject().getKey(); if (currentKey == key) found = true; else currentEl = currentKey > key ? currentEl.lchild() : currentEl.rchild(); } if (currentEl == null) return null; return currentEl.getObject(); Satz: In Zeit O(n) können wir deterministisch das i-t größte Element aus einer Menge der Größe n finden. Bemerkung: Wieso 5-er Gruppen und keine 3-er Gruppen, 7-er Gruppen, ...? Wahl von a? Wahl von c? c = 10 · a, a · n = n5 · Median(5 Elemente) Für 5 Elemente braucht man zur Berechnung des Medians 4 + 3 + 2 = 9 Vergleiche. 6 Balancierte Bäume Mit einem Heap kann man das minimale Element schnell finden und alle anderen Elemente in O(n). Wie kann man binäre Bäume so aufbauen, dass man alle Element in O(n log n) finden kann? 6.1 Bäume Bäume bestehen aus Knoten v, die auf andere Knoten zeigen können. In binären Bäumen zeigt jeder Knoten auf höchstens zwei andere Knoten, seine sogenannten Kinder. Wir untersuchen lchild (linkes Kind) und rchild (rechtes Kind). Der linke Teilbaum von v sind alle Knoten, die über v.lchild erreicht werden können. Dies gilt für den rechten Teilbaum analog. Knoten speichern Objekte, die eindeutig mit einer Zahl n ∈ N als Schlüssel assoziiert sind. Man unterscheidet zwei Arten der Speicherung. Knotenorientiert: Ein Objekt pro Knoten. Für alle v gilt, dass der Schlüssel im linken Teilbaum kleiner ist als der Schlüssel des Objekts v, der wiederum kleiner ist als der Schlüssel von allen Objekten im rechten Teilbaum. Blattorientiert: Ein Objekt pro Blatt. Ein Schlüssel pro Nicht-Blatt (“innerer Knoten”). Für alle v gilt, dass der Schlüssel von Objekten im linken Teilbaum von v kleiner ist als der Schlüssel von v und wieder wiederum kleiner ist als die Schlüssel aller Objekte im rechten Teilbaum von v. Hier: Knotenorientierte Speicherung. Wie können Suche und Einfügen implementiert werden? Einfügen: Zuerst rufen wir Suche(v) auf und lassen uns den letzten besuchten Knoten zurückgeben. Der zuletzt besuchte Knoten hat nur ein Kind. ⇒ Füge Objekt als neues Kind ein. 21 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 6 BALANCIERTE BÄUME Streiche(x) : Zuerst führen wir Suche(x) aus. Ist x ∈ T , endet die Suche in Knoten u mit dem zugehörigen Schlüssel x. Fall 1: u ist ein Blatt ⇒ streiche u Fall 2: u hat nur ein Kind w ⇒ streiche u und setze w an die Stelle von u als Kind im Elternknoten v von u Fall 3: u hat zwei Kinder ⇒ suche den Knoten v mit dem größten Schlüssel im linken Teilbaum (einmal links, dann rechts, bis es nicht mehr geht ⇒ v) ⇒ v hat höchstens ein Kind. Tausche die Objekte von u und v. Lösche dann Knoten mit Fall 1 oder 2. Komplexität der Operationen: O(h), wobei h die Höhe des Baumes ist (entspricht also dem längsten Pfad von der Wurzel zu einem Blatt). Problem: Höhe kann O(n) sein. Idee: 1. Baue Baum von Zeit zu Zeit wieder ausgewogen auf 2. Balancierte Bäume 6.2 AVL-Bäume Definition: Sei u ein Knoten in einem binären Baum. Balance(u) von u bezeichnet die Differenz der Höhen von rechtem und linkem Teilbaum. Definition: Ein binärer Baum heißt AV L-Baum, falls für alle Knoten v ∈ V |Balance(v)| ≤ 1 gilt. Wir wollen nun zeigen, dass die Höhe in einem solchen Baum O(n log n) ist. Dazu definieren wir F IBONACCIBäume rekursiv wie folgt: F IBONACCI-Zahlen sind ebenfalls rekursiv definiert: F0 = 0, F1 = 1, Fn = Fn−1 + Fn−2 ∀n ≥ 2 Folgerung: Tn enthält Fn Blätter. Behauptung: AVL-Baum der Höhe h enthält mindestens Fn Blätter. √ n=0 n = 1: n ≥ 2: Man erhält blattärmsten AVL-Baum, wenn m an jeweils den blattärmsten AVL-Baum der Höhe h − 1 mit dem blattärmsten AVL-Baum der Höhe h − 2 kombiniert. Nach Induktionsannahme hat dieser Baum mindestens Fn−1 + Fn−2 = Fn Blätter. 22 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 6 BALANCIERTE BÄUME Bekannt: Für h ≥ 0 gilt Fh = α = β = αh − β h √ 5 √ 1+ 5 ≈ 1, 6 2√ 1− 5 ≈ −0, 6 2 Lemma: Ein AVL-Baum mit n Knoten hat die Höhe O(log n). Beweis: Der Baum hat höchstens n Blätter. n ≥ Blätter ≥ Fn Wenn h groß genug gewählt wird, erhält man: Fh n ⇒h ⇒h αh √ 2 5 αh √ ≥ 2 5 √ log(2 5 · n) = α ∈ O(log n) ≥ Wie geht jetzt das Einfügen und Streichen? Zusätzlich assoziiert zu Knoten ist seine Balance. Einfügen: Durch das Einfügen eines Knotens w können die Balancen einiger Vorfahren die Werte ±2 annehmen. Sei u der tiefste dieser Vorfahren und sei ohne Beschränkung der Allgemeinheit Balance(u) = 2, so wurde w im rechten Teilbaum eingefügt. Sei nun v das rechte Kind von u, dann muss sich die Höhe zusätzlich erhöht haben, also ist die Balance von v = ±1. Beachte: 1. Rotation bewahrt die Suchbaumeigenschaft. 2. u und v haben nach der Rotation eine Balance von 0 3. Alle Knoten in A, B und C behalten ihre (legale) Balance 4. Die Höhe von v ist nach der Rotation gleich der Höhe von u vor dem Einfügen von w. Also sind nach der Rotation alle Vorfahren von w balanciert. 23 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 6 BALANCIERTE BÄUME Zusammenfasssung: Balance(u) = H(rT B(v)) − H(lT B(v)), wobei H(t) die Höhe des Baums t ist und rT B(t) bzw. lT B(t) rechter bzw. linker Teilbaum von t sind. AVL- Bäume haben für jeden Knoten v eine Balance(v) ≤ 1. Problem: Balance kann beim Einfügen und Streichen verloren gehen. Es gibt 4 Rotationsoperationen: Sei u der tiefste Knoten mit Imbalance. • Balance(u) = 2, Balance(v) = 1 ⇒ Rotation.links(u) • Balance(u) = −2, Balance(v) = −1 ⇒ Rotation.rechts(u) • Balance(u) = 2, Balance(v) = −1 ⇒ Doppelrotation.rechtsLinks(u) • Balance(u) = −2, Balance(v) = 1 ⇒ Doppelrotation.linksRechts(u) Beachte: Diese Operationen können durch Umhängen von Zeigern in O(1) implementiert werden. Streichen von Elementen: Streichen erstmal wie im einfachen Suchbaum. Imbalancen wie beim Einfügen von unten nach oben (tiefster Knoten zuerst auflösen). Vorsicht: Für |Balance(u)| = 2 und |Balance(v)| = 1 hat der Teilbaum an v nach der (Doppel-)Rotation eine geringere Höhe als vorher der von u. Daher muss die Suche nach Imbalancen nach oben fortgesetzt werden. Satz: Balancierte Bäume wie AVL-Bäume erlauben Suchen, Einfügen und Streichen in O(log n) Zeit, wobei n die Knotenanzahl ist. 6.3 B-Bäume Für große Datenmengen, die nicht in den Hauptspeicher passen, sind AVL-Bäume ungeeignet. Hier kommen B-Bäume zum Zuge, die für Zugriff auf externen Speicher entworfen wurden. 24 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 6 BALANCIERTE BÄUME Definition: Ein B-Baum der Ordnung k ≥ 2 ist ein Suchbaum • dessen Blätter alle dieselbe Tiefe haben • dessen Wurzel mindestens zwei und höchstens 2k − 1 Kinder hat • in dem jeder andere (innere) Knoten mindestens k Kinder und höchstens 2k − 1 Kinder hat Lemma: Sei T ein B-Baum der Ordnung k mit Höhe h und n Blättern, dann gilt: 2k h−1 ≤ n ≤ (2k − 1)h Merksatz für “Höhe/Tiefe”: Wie hoch stehen Sie in Ihrer Firmenhierarchie? “Unter mir sind 4 Leute.” Wie tief stehen Sie? “Über mir sind x Leute.” Logarithmieren der Formel aus dem Lemma ergibt log2k−1 n ≤ h ≤ 1 + logk n 2 ⇒ Höhe vom B-Baum liegt in O(log n) Operationen: Zugriff (Suche), Einfügen, Streichen Annahme: Wir speichern knotenorientiert. Betrachte Knoten u mit k ≤ l ≤ 2k − 1 Kindern. Die Schlüssel k1 bis kl sind sortiert. Sei Ti der i-te Teilbaum von u, dann gilt für alle Knoten v ∈ Ti und alle Schlüssel s in v: • s ≤ si , falls i = 1 • si−1 ≤ s ≤ si , falls 1 < i < l • si−1 < s, falls i = l Suche(a, S) Starte in Wurzel w und suche den kleinsten Schlüssel si in w mit a ≤ si Wenn a = si ⇒ Element gefunden Falls si =null: Suche rekursiv weiter im rechtesten Kind Sonst: Suche rekursiv weiter im Teilbaum Ti 25 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 6 BALANCIERTE BÄUME ) Laufzeit: O(logk n · k ) kann verbessert werden zu O(logk n · log k |{z} | {z } | {z } binäre Suche Höhe lineare Suche Einfügen(a, S) (Erfolglose) Suche endet in Blatt b mit Elternknoten v, der l Kinder hat. < si (falls existent). Füge neuen Sei wieder si der kleinste Schlüssel mit a Schlüssel (samt Objekt) vor si ein (oder ganz rechts) und füge neues Blatt b’ ein, auf das der Pointer nach a zeigt. Falls l vor dem Einfügen < 2k − 1 ist: Alles okay. Falls l=2k-1: Spalte v in zwei Knoten v’ und v” mit jeweils k Kindern. Vor der Spaltung enthält v 2k-1 Schlüssel. Nimm den mittleren Schlüssel und füge ihn im Vaterknoten an die richtige Stelle Dadurch kann es rekursiv nach oben zu weiteren Aufsplittungen kommen. Falls die Wurzel gesplittet werden muss, wird oberhalb von der alten Wurzel ein neuer Knoten kreiert, der als einzigen Schlüssel den mittleren Schlüssel der alten Wurzel bekommt und auf v’ und v” zeigt. (Daher gilt für die Wurzel, dass sie mindestens zwei Kinder haben muss.) Streichen eines Schlüssels aus einem B-Baum: Annahme: a=sj kommt in Knoten u vor Mittels Suche(a, S) suche zunächst Knoten u, in dem a als Schlüssel sj vorkommt. Allgemeine Situation: Fallunterscheidung: 1. Tj ist ein Blatt, dann lösche sj und Tj . 2. Tj ist kein Blatt. Sei s der größte Schlüssel in Tj , dann ersetze sj durch s und lösche s samt seinem Blatt in Tj . Sei v der Knoten in Tj mit dem größten Schlüssel und r die ursprüngliche Anzahl der Kinder von v mit k ≤ r ≤ 2k − 1. 1. Falls r > k (bzw. r > 2, falls v die Wurzel ist), ist alles okay, denn die B-Baum-Eigenschaft bleibt erhalten. 2. Sei r = k und v nicht die Wurzel: Sei w der Elternknoten von v und v 0 das Kind links von v und s0 der Schlüssel in w zwischen diesen beiden Kindern. Verschmelze nun v 0 und v: 26 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 6 BALANCIERTE BÄUME Wegen k ≤ l ≤ 2k − 1 hat vneu jetzt mindestens k+k − 1 = 2k − 1 Kinder und höchstens 2k − 1 + k − 1 = 3k − 2 Kinder. Falls vneu mehr als 2k − 1 Kinder hat, wird er wieder (wie beim Einfügen auch) gesplittet. Jedes der Kinder hat dann mindestens k Kinder und höchstens 3k 2 − 1 Kinder. Anmerkung: Hat vneu genau 2k − 1 Kinder, darf nicht gespalten werden. Jedoch hat nun w ein Kind verloren und so muss rekursiv nach oben fortgefahren werden. Zusammenfassung: B-Bäume Zugriff (Suchen), Einfügen und Streichen kann in B-Bäumen der Ordnung k, die n Schlüssel verwalten, in Zeit O(k · log n) durchgeführt werden. 6.4 Kantenmarkierte Suchbäume (“Tries”) Bisher haben wir Suchbäume so organisiert, dass der direkte Vergleich von Schlüsseln den weiteren Suchpfad im Baum bestimmt. Für sehr lange Schlüssel über einem Alphabet Σ sind Vergleiche der Art “1. Buchstabe von x <1. Buchstabe von y?” sinnvoller. Idee: Jeder Knoten hat wieder mehrere Kinder, aber für jeden Buchstaben des Alphabets höchstens einen. Definition: Sei Σ ein festes, endliches Alphabet. 1. Ein Baum heißt Σ-markiert, falls gilt: (a) Jede Kante (u, v) ∈ E ist mit einem Buchstaben a ∈ Σ markiert. v heißt dann a-Sohn von u. (b) Jeder Knoten hat höchstens einen a-Sohn. 2. Sei T = (V, E) ein Σ-markierter Baum, dann ist φ : V → E ? definiert durch: φ(v) = (leeres Wort), wenn v Wurzel von T φ(v) = φ(u)a, wenn v der a-Sohn von u ist Wir sagen: v definiert das Wort φ(v) ∈ Σ? . φ(v) ist das Wort, das entsteht, wenn man die Buchstaben entlang des Pfades P (w, v) von der Wurzel w zum Knoten v konkateniert. 3. Sei S ⊆ Σ? , dann heißt T kantenmarkierter Suchbaum für S (engl. “Trie”) falls gilt: (a) Für jedes Blatt v ∈ T ist φ(v) das Anfangswort (Präfix) eines Wortes x ∈ S. (b) Zu jedem x ∈ S gibt es genau ein Blatt v, so dass φ(v) das Anfangswort von x ist. Das Blatt v trägt als Information dasjenige Element x ∈ S, das mit φ(v) beginnt. ⇒ blattorientierte Speicherung 27 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 6 BALANCIERTE BÄUME Beachte: Einen Trie gibt es nur für solche Mengen S, die präfixfrei sind, d.h. kein x ∈ S ist ein echtes Anfangswort eines y ∈ S. Präfixfreiheit lässt sich stets erreichen, indem ein neuer Buchstabe zu Σ hinzugefügt wird, z.B. #. Dieser Buchstabe markiert ab jetzt das Ende eines Wortes, so dass z.B. BAU # nicht mehr Präfix von BAU M # ist. Auf Σ sei eine Ordnung definiert, z.B. a < b < · · · < z (lexikalische Sortierung). Seien nun die Söhne jedes Knotens nach dieser Ordnung sortiert, so sind in den Blättern die Elemente von S aufsteigend von links nach rechts sortiert. Nachteile von Tries: • Tries sind oft sehr dünn besetzt, d.h. fast alle Knoten haben weniger als |Σ| Kinder. Diese Kinder könnte man verwalten 1. als Array: Vorteil: Nachteil: 2. als Liste: Vorteil: Nachteil: direkter Zugriff auf den nächsten Buchstaben per Index P Platzbedarf |Σ| · #Knoten statt nur v∈V Grad(v) P Platzbedarf v∈V Grad(v) Nur lineare Suche beim Zugriff • Es kann lange Wege im Baum geben ohne Verzweigungspunkte. 6.5 PATRICIA-Bäume Idee: 1. Setze Σ = {0, 1} und betrachte Binärcodierung von Buchstaben und Wörtern. 2. Ziehe die Wege ohne Verzweigung zu einem Knoten zusammen. Kennzeichne diesen Knoten mit der Anzahl der übersprungenen Buchstaben. ⇒ PATRICIA-Bäume (Practical Algorithms To Retrieve Information Coded In Alphanumerics) • Patricia-Bäume sind geeignet für sehr lange Schlüssel unterschiedlicher Länge. • Sie sind binäre Bäume, deren linker Sohn immer der 0-Sohn ist (rechts der 1-Sohn). • Jeder innere Knoten hat ein Feld ’skip’, das angibt, wie lang der Weg ist, den dieser Knoten repräsentiert, d.h. wie viele Buchstaben überlesen werden müssen. • In den Blättern stehen die Schlüssel. Beispiel: Suche(THAT, P), P ... Patricia-Baum (vom Beiblatt). THAT: 10111010000000110111 1 + 0 = 1. Buchstabe= 1 ⇒rechts 1 + 11 = 12. Buchstabe= 0 ⇒links 12 + 1 = 13. Buchstabe= 0 ⇒links ⇒ Steht im gefundenen Blatt und ist also im Baum enthalten. Beispiel: Suche(USA, P), P ... Patricia-Baum (vom Beiblatt). USA: 110001011000001 1 + 0 = 1. Buchstabe= 1 ⇒rechts 1 + 11 = 12. Buchstabe= 0 ⇒links 12 + 1 = 13. Buchstabe= 0 ⇒links ⇒ Steht nicht im Blatt und ist daher nicht im Baum vorhanden. 28 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 6.6 6 BALANCIERTE BÄUME Randomisierte Suchbäume: Skiplists (W. Pugh) Idee von Skiplists ist, dass eine sortierte Liste mit zusätzlichen, in einer bestimmten Weise zufällig gewählten Verlinkungen die wesentlichen Operationen “Suchen”, “Einfügen” und “Löschen” in O(log n) unterstützt. Gegeben: • Eine sortierte Teilmenge S aus einem Universum U mit einer natürlichen Ordnung, d.h. S = {x1 , x2 , . . . , xn } mit xi < xi+1 für alle 1 ≤ i < n. • Eine Levelzuweisung L : S → N+ Die Levelzuweisung definiert Teilmengen, die Levelmengen Li , so dass x ∈ Li ⇔ L(x) ≥ i. Es gilt also S = L1 ⊇ L2 ⊇ · · · ⊇ Lv ⊇ ∅, wobei Lv das höchste, nicht-leere Level repräsentiert. Eine Skiplist wird nun wie folgend gebildet: • Definiere einen Anfangsknoten, den “header”, der jeweils auf das kleinste Element jedes Level Li zeigt. • Für alle Li : Jedes Element in Li zeigt auf das nächstgrößere Element in Li . • Diese Zeiger sind mit i markiert und pro Knoten absteigend sortiert. • Jedes größte Element im Level Li zeigt auf “null”. Wie kann die Levelzuweisung erfolgen? Zufällige Levelzuweisung: Wähle für die Skiplist 0 ≤ p ≤ 1. In dem Moment, in dem ein Element x eingefügt wird, zieh so lange eine positive Zufallszahl ≤ 1 bis das Ergebnis ≥ p. Die Anzahl der Züge ist der Level l, der x zugewiesen wird. public int randomLevel (double p) int level = 1; Random random = new Random(11265); while random.nextDouble() < p ++level; return level; Die zurückgegebene Levelzuweisung folgt einer geometrischen Verteilung, d.h. dass die Wahrscheinlichkeit, dass level = k ist, ist: P [level = k] = pk−1 (1 − p) Und der Erwartungswert einer solchen Verteilung ist E(level) = ∞ X k · pk−1 · (1 − p) = k=1 1 1−p ⇒ Erwarteter Platzverbrauch einer solcher Art zufälligen Skiplist ist O(n). Wie hoch ist die höchste erwartete Levelzuweisung? Lemma: Die Zahl der Level v bei zufälliger Levelzuweisung hat den Erwartungswert O(log n). Es gilt sogar, dass v ∈ O(log n) mit hoher Wahrscheinlichkeit. [“Mit hoher Wahrscheinlichkeit” bedeutet, dass das Gegenereignis höchstens Wahrscheinlichkeit 29 1 n hat.] www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 6 BALANCIERTE BÄUME Beweis: v = max {L(x)|x ∈ S} 1 . Die Wahrscheinlichkeit, dass L(x) größer als ein L(x) ist geometrisch verteilt mit Erwartungswert 1−p t gewähltes t ∈ N ist, ist < p . Da die Levelzuweisung für alle Elemente unabhängig ist, ist die Wahrscheinlichkeit, dass es ein Element x (von n Elementen) gibt mit L(x) > t kleiner oder gleich n · pt ist (für p = 21 also 2nt ). Wir setzen t = α · log n [α > 2]. n 1 1 1 ⇒ P [v > α · log n] ≤ 2α·log n = nα−1 < n für p = 2 . ⇒ Höhe der Skiplist mit hoher Wahrscheinlichkeit in O(log n) ist. Suche(y, S) Die Zeiger eines Knotens seien absteigend nummeriert mit der Nummer des Levels des Elements, auf das der Zeiger zeigt. Starte im header mit levelpointer auf das maximale, nicht leere Level v. In dem aktuellen Knoten überprüfen wir für die Zeiger kleiner oder gleich dem levelpointer in absteigender Reihenfolge, ob der Wert des Elements, auf das sie zeigen, kleiner oder gleich y ist. Gibt es keinen solchen Zeiger, dann ist y nicht in S enthalten. Ansonsten setze den levelpointer auf das Level des gefundenen Zeigers und folge dem Zeiger auf das nächste Element. Setze die Suche dort rekursiv fort. Laufzeit: Wir unterteilen die Suchstrecke in zwei Teile: a . . . Wegstrecke zum maximalen Level Lv b . . . Wegstrecke zu niedrigeren Leveln wird gebunden durch die Anzahl der Elemente in Lv . Wir betrachten dazu die Anzahl von Knoten in Level v 0 : v 0 = log p1 n a Ganz allgemein: Die Wahrscheinlichkeit, dass ein Knoten x in Level k + 1 ist, ist < pk . Die Anzahl von Elementen in Level k ist erwartet ≤ n · pk = 1. Wir suchen k, so dass erwartet nur ein Element in dem Level ist. k 1 n= ⇔ log p1 n = k p Wir machen eine Rückwärtsanalyse, d.h. wir gehen von dem gefundenen Element zum höchsten Level. Sei also C(k) die erwarteten Kosten, um in einem Knoten mit Level ≥ k zu kommen, mit C(0) = 0, C(1) = 1. Um in einem Knoten mit Level k zu sein, waren wir vorher (Rückwärtsanalyse!) entweder in einem anderen Knoten in dem gleichen Level oder in dem gleichen Knoten auf Level k − 1. b: Da die Wahrscheinlichkeit, dass der zuletzt besuchte Knoten in Level k − 1 gleichzeitig auch in Level k ist, ist p, dass er es nicht ist 1 − p. C(1) = 1 C(k) = (1 − p)(1 + C(k)) + p (1 + C(k + 1)) C A B D A E A . . . Kosten für Folgen von Zeiger B . . . Wir waren schon in Level k C . . . Wahrscheinlichkeit, dass wir in Level k bleiben D . . . Wahrscheinlichkeit, dass wir ein Level aufsteigen E . . . Kosten, um Level k − 1 zu erreichen C(k) = k p (Umformungen) Da das maximale k in O(log n) liegt, ist auch b in O(log n) und damit der gesamte Suchpfad in O(log n). 30 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 7 GRAPHENALGORITHMEN Einfügen(y, S) Bestimme das Level L(y) wir beschrieben. In jedem Level suche Einfügestelle, lege 2 · L(y) Zeiger neu an. ⇒ Laufzeit O(log n) Streiche(y, S) Führe zuerst Suche(y, S) aus. Lege in jedem der besuchten Level die Zeiger auf y auf den Nachfolger von y. ⇒ Laufzeit O(log n) Satz: In einer zufälligen Skiplist für die Menge S der Größe n können Suchen, Einfügen und Streichen in erwarteter Zeit O(log n) implementiert werden. Da keinerlei Rebalancierungen ausgeführt werden müssen, ist diese Datenstruktur sehr einfach. 7 Graphenalgorithmen Bisher haben wir uns hauptsächlich mit ungerichteten Graphen beschäftigt. Wenn Kanten nicht eine Menge von Knoten, sondern ein geordnetes Paar darstellen, dann nennen wir den Graph gerichtet. Das Element e = (v, w) ∈ E heißt Kante von v nach w. v ist der Startknoten und w der Zielknoten. w ist Nachbar(-knoten) von v bzw. w ist adjazent zu v. Für einen Pfad P (v, w) in einem gerichteten Graphen muss die Kantenfolge aus Kanten mit der richtigen Richtung bestehen, d.h. P (v, w) = (e1 , e2 , . . . , ek ), dann muss e1 = (v, v1 ) ek = (vk−1 , w) Für alle 1 < i < k ist ei = (vi−1 , vi ) Allgemein: Startknoten der (1 < i ≤ k)-ten Knte ist Zielknoten der (i − 1)-ten Kante. Beispiel: Der Pfad (a, d) existiert. Nämlich P (a, d) = ((a, b), (b, c), (c, d)). Der Pfad P (d, a) existiert nicht. Ein gerichteter Graph heißt zyklisch, wenn er mindestens einen gerichteten Zykel enthält und sonst azyklisch (engl. DAG = directed acyclic graphs). Falls es einen gerichteten Pfad von v nach w in G gibt, so ? schreibt man v → w. G Konventionsgemäß bzw. mathematisch korrekt: • (v, w): gerichtete Kanten • {v, w}: ungerichtete Kanten 31 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 7.1 7 GRAPHENALGORITHMEN Darstellung von Graphen Sei G = (V, E) mit V = {0, 1, . . . , n}. Es gibt zwei Darstellungsarten für Graphen: 1. Darstellung als Adjazenzmatrix A = (aij ): 0 ≤ i, ( j<n 1 , falls e = (i, j) ∈ E aij = 0 , sonst Bei ungerichteten Graphen ist die Adjazenzmatrix symmetrisch. Der Platzbedarf für diese Darstellung beträgt O(n2 ). Das ist günstig, wenn die Anzahl der Kanten m ∈ O(n2 ). Aber: Insbesondere in realen Netzwerken ist E “dünn”, d.h. die meisten Knoten haben nur sehr wenige Kanten zu anderen Knoten. ⇒ m ≈ O(n) Insbesondere zwei Graphenklassen haben nur O(n) Kanten: • Bäume: n − 1 Kanten • Planare Graphen sind solche, die sich auf der Ebene zeichnen lassen, ohne dass sich Kanten kreuzen. Nicht-planare, ungerichtete Graphen sind beispielsweise: Vollständig Vollständiger bipartiter Graph Graph 5-Clique Ein Graph heißt “bipartit”, wenn sich seine Knotenmenge V in zwei Teilmengen teilen lässt, so dass es zwischen Knoten der selben Teilmenge keine Kanten gibt. Mitteilung: Ein planarer Graph hat m ≤ 3n − 6 Kanten , also m ∈ O(n). 2. Darstellung als Adjazenzliste: Speichere für jeden Knoten v seine Nachbarn in einer verketteten Liste. Wenn G gerichtet ist, hat jeder Knoten zwei Listen: • InAdj(v) = {w ∈ V |(w, v) ∈ E} • OutAdj(v) = {w ∈ V |(v, w) ∈ E} Wenn G ungerichtet ist: • Adj(v) = {w ∈ V | {w, v} ∈ E} Platzbedarf: O(n + m) Nachteil: Zugriffszeit auf eine Kante ist abhängig von der Listenlänge, also dem Knotengrad. Bei gerichteten Graphen unterscheiden wir zwei Knotengrade: • Eingangsgrad indegree(v) = |InAdj| • Ausgangsgrad outdegree(v) = indegree(v) = |OutAdj| Ein gerichteter Graph ist ein gerichteter Baum, wenn gilt: (a) ∃v0 ∈ V mit indegree(v0 ) = 0 und (b) ∀v ∈ V \ {v0 } : indegree(v) = 1 und (c) Graph muss azyklisch sein 32 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 7.2 7 GRAPHENALGORITHMEN Topologisches Sortieren Sei G = (V, E) ein gerichteter Graph, dann heißt die Abbildung num : V → {1, 2, . . . , n} mit n = |V | topologische Sortierung, falls für alle e ∈ E gilt: (e = (u, v)) : num(u) ≤ num(v) Lemma: G besitzt genau dann eine topologische Sortierung, wenn er azyklisch ist. Beweis: “⇒”: Angenommen G ist zyklisch, dann ist (v0 , v1 , . . . , vk = v0 ) ein Kreis. Es muss gelten: num(v0 ) < num(v1 ) < · · · < num(vk ) = num(v0 ) Widerspruch! “⇐”: Sei G azyklisch. Behauptung: G enthält einen Knoten v mit indeg(v) = 0 (# eingehender Kanten). Beweis per Induktion: • Anfang: |V | = 1: klar • Schritt: |V | > 1 : Entferne beliebigen Knoten v ∈ V und erhalte einen Graph G0 = (V 0 , E 0 ) mit V 0 = V \ {v}, E 0 = E ∩ (V 0 × V 0 ). Nach Annahme ist G0 azyklisch und enthält ein v 0 mit indeg(v 0 ) = 0. Entferne v 0 aus G und erhalte G00 , der wieder ein v 00 enthält mit indeg(v 00 ) = 0. Lies den Beweis nochmals mit v = v 00 . Es gilt: Entweder ist indeg(v 0 ) = 0 in G oder indeg(v 00 ) = 0 in G, weil andernfalls: (v 00 , v 0 ) ∈ E und (v 0 , v 00 ) ∈ E Das kann aber nicht sein, da G azyklisch ist. topSort(G) √ |V|=1: |V|>1: wähle v mit indeg(v)=0 entferne v und weiter rekursiv auf G’=(V’,E’) sei num’: V’→{1,...,|V’|} eine topologische Sortierung für G’ ( num0 (w) + 1 , falls w 6= v Dann num(w)= 1 , falls w = v 33 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 7 GRAPHENALGORITHMEN count ← 0 while ∃v ∈ V mit indeg(v)=0 do count++ num(v)←count streiche v und alle ausgehenden Kanten endwhile if count < |V| then “G zyklisch” Ziel: O(|{z} n + |{z} m ) ⇒ Adjazenzlisten! |V | |E| Ausgehende Kanten löschen: O(outdeg(v)) P Alle löschen: v outdeg(v) = O(n + m) Benutze in-zähler-Array: beim Löschen der Kante (v, w) dekremtentiere inzaehler[w], Merke alle Knoten mit inzaehler = 0 in Menge ZERO (als Stack) ⇒ O(n + m) 7.3 Durchmusterungsalgorithmen Bestimme alle Knoten, die von einem gegebenen s ∈ V erreichbar sind. S ← {s} Markiere alle Kanten als unbenutzt. while ∃(v, w) ∈ E mit v ∈ S und (v,w) unbenutzt do sei e=(v,w) eine solche Kante markiere e als benutzt S←S ∪ {w} endwhile Invariante: In S sind diejenigen Knoten, die über benutzte Kanten erreichbar sind. Probleme: 1. Realisierung benutzt ↔ unbenutzt 2. Finde geeignete Kante 3. Realisierung von S Lösung: 1. Halte für jede Adjazenzliste einen Trennzeiger p(v) zwischen benutzten und unbenutzten Kanten aus V . Verschiebe ihn nach rechts. v → w1 → w2 → w3 → w4 → w5 | {z } ↑ | {z } benutzt p(v) unbenutzt 2. Wir halten S̃ ⊆ S. In S̃ stehen Knoten, für die es noch unbenutzte ausgehende Kanten gibt. 34 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 7 GRAPHENALGORITHMEN 3. “Initialisierung”, “Einfügen” und “Ist w ∈ S?” sind Operationen. Implementiere S als Boolean-Array, ⇒ jeweils in O(1) Operationen auf S̃: “Initialisierung”, “S̃ = ∅?”, “Einfügen”, “Wähle v ∈ S̃ beliebig und streiche es eventuell” Implementiere S̃ als Stack oder als Queue oder als Heap. Allgemeiner Durchmusterungsalgorithmus siehe Extrablatt Durch Änderung von Zeile 9 (Addieren von w an letzter/erster Stelle) ändert sich das Durchmusterungsverhalten stark. letzte Stelle: Breitensuche (Breadth First Search, BFS) erste Stelle: Tiefensuche (Depth First Search, DFS) 7.3.1 Tiefensuche Implementierung ist speicherplatzineffizient (wegen der Zeiger). Rekursive Form der Tiefensuche: 1 void DFS (Node v, boolean[] visited) 2 3 4 5 6 7 visited[v] = true; for all (v, w) ∈ E do if not visited[w] DFS(w, visited); endif endfor Überzeugen Sie sich von der Äquivalenz der Algorithmen. Die Tiefensuche unterteilt die Kanten des Graphen in vier Klassen T , B, F , C je nach Art des Besuches. Sei (v, w) die in Zeile 3 betrachtete Kante, dann (A) gehört (v, w) zu den Baumkanten T (tree), wenn w vorher nicht nicht besucht wurde. ? (B) gehört (v, w) zu den Vorwärtskanten F (forward), falls w schon besucht wurde und ∃v → w, d.h. T wenn es einen Pfad von v nach w in T gibt. ? (C) gehört (v, w) zu den Rückwärtskanten B (backward), falls w schon besucht wurde und ∃w → v. T ? ? T T (D) gehört (v, w) zu den Querkanten C (cross), falls weder v → w, noch w → v existieren. Wir erweitern den rekursiven Algorithmus um df snum[v] und compnum[v], wobei df snum[v] angibt, als wievielter Knoten v zum ersten Mal besucht wurde. compnum[v] angibt, als wievielter Knoten v’s DFS-Aufruf beendet war (completed). 35 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 7 GRAPHENALGORITHMEN for all v ∈ V do visited[v] = false Initialisiere dfsnum[v] und compnum[v] auf 0 endfor Setze globale Zähler z1, z2 auf 0 Initialisiere leere Mengen T, B, C, F for all v ∈ V do if not visited[v] then DFS(v) endfor Laufzeit: O(n + m), da a) die Einzellaufzeit eines Aufrufs ohne Rekursion O(1 + outdegree(v)) ist. P b) für jeden Knoten nur einmal ein DSF-Aufruf gemacht wird: O( v∈V (1 + outdegree(v)) = O(n + m) Lemma: Eigenschaften, Teil 1 a) Kantenklassen T , B, C, F bilden eine Partition der Kantenmenge E. (Beweis ist trivial.) b) T entspricht dem Wald (Menge von Bäumen) der rekursiven Aufrufe. (Beweis ergibt sich einfach aus dem Algorithmus.) ? c) v → w ⇔ df sum[v] ≤ df snum[w] ∧ compnum[w] < compnum[v] T ? ? T T d) Seien v, w, z ∈ V mit v → w, (w, z) ∈ E und ¬(v → z), dann gilt: (i) df snum[z] < df snum[v] (ii) (w, z) ∈ B (iii) compnum[z] > compnum[v] ⇔ (w, z) ∈ B (iv) compnum[z] < compnum[v] ⇔ (w, z) ∈ C Beweis: ? c) v → w T ⇔ Aufruf DF S(w) ist geschachtelt in DF S(v) ⇔ df num[v] ≤ df snum[w] ∧ compnum[w] ≤ compnum[v] d) Schrittweise: 36 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 7 GRAPHENALGORITHMEN ? • aus v → w folgt, dass dsf num[v] ≤ df snum[w] T • aus (w, z) ∈ E folgt, dass DF S(v) aufgerufen wird, bevor DF S(w) und DF S(v) enden. ? • ¬(v → w) ⇔ DF S(z) nicht in DF S(v) geschachtelt ist (folgt aus c). T ⇒ DF S(z) startet vir dem Aufruf DF S(v) ⇒ df snum[z] < df snum[v] (i bewiesen) • Wegen df num[z] < df snum[w] folgt, dass (w, z) ∈ / T und wegen (w, z) ∈ / T ∧ df snum[z] < df snum[v] < df snum[w] folgt, dass (w, z) ∈ / F ⇒ (w, z) ∈ B ∪ C (wegen a). (Beweis für ii) Def ? ? ? ? • (w, z) ∈ B ⇔ z → w ⇔ z → v [da v → w existiert und wir wissen, dass ¬(v → z) ist, muss das T T T T die einzige Möglichkeit sein) ⇒ compnum[z] > compnum[v] (Beweis zu iii) Def ? ? • (w, z) ∈ C ⇔ weder w → z noch z → w T T Wir wissen, dass df snum[z] < df snum[w]. Wegen c folgt nun, dass compnum[z] < compnum[w] < ? compnum[v] (denn sonst gäbe es z → w) (Beweis zu iv). T Lemma: Eigenschaften, Teil 2 Sei (v, w) ∈ E e) (v, w) ∈ T ∪ F ⇔ df snum[v] < df snum[w] f) (v, w) ∈ B ⇔ df snum[w] < df snum[v] ∧ compnum[w] > compnum[v] g) (v, w) ∈ C ⇔ df snum[w] < df snum[v] ∧ compnum[w] < compnum[v] Beweis: ? e) (v, w) ∈ T ∪ F ⇒ v → w ⇒ df snum[v] ≤ df snum[w] T Sei nun df snum[v] ≤ df snum[w]. Da (v, w) ∈ E, muss DF S(w) vor Abschluss von DF S(v) aufgerufen werden. ⇒ echte Schachtelung der Aufrufe ? ⇒v→w T ⇒ (v, w) ∈ T ∪ F 37 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 7 GRAPHENALGORITHMEN Bemerkungen: 1. Ob eine Kante e = (v, w) in T , B, C, F liegt, kann also (Lemma) durch Vergleich der df snum und compnum festgestellt werden. 2. In azyklischen Graphen gibt es keine Rückwärtskanten, d.h. ∀(v, w) ∈ E compnum[v] > compnum[w]. ⇒ Nummerierung num(v) = n + 1 − compnum[v]ergibt eine topologische Sortierung. 7.3.2 Starke Zusammenhangskomponenten (SZKs) Definition: Ein gerichteter Graph G = (V, E) heißt stark zusammenhängend genau dann, wenn ∀v, w ∈ ? ? V gilt: v → w und w → v. E E Die maximal stark zusammenhängenden Teilgraphen von G heißen starke Zusammenhangskomponenten (Maximal: Es kann keine Kante hinzugefügt werden ohne dass diese Bedingung verletzt wird.) Idee für Algorithmus: Erweitere DFS Sei G0 = (V 0 , E 0 ) der bisher erforschte Teil von G. Verwalte die starken Zusammenhangskomponenten von G: Starte V 0 = {S}, E 0 = ∅, SZK = {{S}} Invariante: SZK enthält in jedem Schritt die SZK von G0 . Sei nun V 0 eine sich im Verlauf ergebende Teilmenge aus V mit v ∈ V 0 und (v, w) der nächsten Kante in der DFS. Ist w ∈ / V 0 , d.h. (v, w) ∈ T , dann erweitere SZK um {w}. Ist w ∈ V 0 , dann füge (u.U.) mehrere SZKs zu einer zusammen. Welche SZKs müssen zusammengefügt werden? Notwendige Bezeichnungen: • SZK Ki heißt abgeschlossen, wenn alle DFS Aufrufe für Knoten in Ki abgeschlossen sind. • Die Wurzel von Ki ist der Knoten mit der kleinsten df snum in Ki . • Eine Teilfolge vonb Knoten, sortiert nach ihrer df snum heißt unfertig, falls ihre DFS-Aufrufe aufgerufen, aber ihre SZK noch nicht abgeschlossen ist. • Die Menge W enthalte die nach df snum sortierte Folge von Wurzeln der noch nicht abgeschlossenen SZKs. Beobachtungen am Beispiel: ? 1. ∀v unfertig: v → g E 2. @(v, w) ∈ E: v ∈ abgeschlossene Komponenten, w ∈ nicht abgeschlossene Komponenten Von g ausgehende Kanten: (g, d) ∈ C: Tut nichts, d liegt in abgeschlossener Komponente. (g, b) ∈ B: Vereinigt 4 Komponenten {b, c} {e} {f } {g} ⇒ unfertig (g, h) ∈ T : Erzeugt eine neue Komponente. 38 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 7 GRAPHENALGORITHMEN Proposition: 1. Eine SZK ist maximal in G, wenn sie abgeschlossen ist. 2. Eine SZK ist abgeschlossen, wenn der DFS-Aufruf ihrer Wurzel abgeschlossen ist. Beweis: 1. Da von der SZK keine unbekannten Kanten mehr ausgehen können, kann keine weitere Kante von ihr erreicht werden. 2. Trivial. Verallgemeinerung: Implementiere Menge unf ertig und Menge W (Wurzeln) als Stacks (LIFO, last in first out), so dass: (v, w) ∈ T : push(w, unf ertig) push(w, W ) (v, w) ∈ C ∪ F : Tue nichts. (v, w) ∈ B: Vereinige alle SZK, ausgehend von SZK(v), deren Wurzeln z df snum[v] > df snum[w] while dfsnum[top(w)] > dfsnum[w] do pop(w) endwhile D.h. wir entfernen die anderen Wurzeln aus dem Stack. Beispiel: ⇒ K2 , K3 und K4 werden vereinigt durch pop(w) bis top(w) =Wurzel der SZK(w) Wenn DF S(v) abgeschlossen ist und v = top(w) ist, wird die SZK ausgegeben (wegen Preposition). do z = pop(unfertig) print(z) until (z = v) pop(w) 39 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 7 GRAPHENALGORITHMEN Korrektheit: Invarianten (I1 ) @(v, w) ∈ E mit v in den abgeschlossenen Komponenten und w in den nicht abgeschlossenen Komponenten. (I2 ) Knoten aller nicht abgeschlossenen SZKs (sortiert nach df snum) liegen in einem Pfad in E 0 , ihre Wurzeln in einem Baumpfad. (I3 ) Knoten jeder nicht abgeschlossenen Komponente Ki bilden ein “Intervall” im Stack unf ertig, wobei das zu unterst liegende Element die Wurzel von Ki ist. Zu zeigen: Invarianten bleiben beim Einfügen von Kanten erhalten. 1. Fall: (v, w) ∈ T (I1 ): Es wird keine SZK abgeschlossen (I2 ): Sei r die Wurzel der Komponente von r. Da die Wurzel einer SZK keinstes df snum und ? größtes compnum hat, folgt aus Lemma c): v → w T ? Wegen (v, w) ∈ T gilt: r → w. T Wegen der Induktionsvorraussetzung bleibt I2 erhalten. (I3 ): Trivial. 2. Fall: (v, w) ∈ B (I1 ): Es wird keine SZK abgeschlossen. (I2 ): Pfad wird verkürzt. (I3 ): Löschen der obersten Wurzeln vereinigt Intervalle zu einem neuen. Da vorher I2 galt, bildet dieses Intervall eine eigene Komponente. Satz: SZKs eines gerichteten Graphen können in O(n + m) berechnet werden. Bemerkungen: 1. Der Test, ob ein Knoten w ∈ unf ertig ist (für die Frage, ob SZKs vereinigt werden müssen) kann durch ein bool’sches Array implementiert werden. 2. Jeder Knoten wird höchstens einmal in unf ertig und W aufgenommen. I N DEN V ORLESUNGEN AM 28.06.2007, 03.07.2007 UND 05.07.2007 KAM EINE P RÄSENTATION ZUM E INSATZ , DIE SICH AUF DEN FOLGENDEN S EITEN FINDET: 40 www.bioinfoblog.de Graphentheorie und Netzwerkanalyse Eine kleine Reise durch eine wandlungsfähige wissenschaftliche Methode © by Sheep N. Dalton http://www.thepurehands.org/fractional/ Komplexe Systeme – überschaubar gemacht als Graphen • Viele Systeme sind komplex, d.h., dass diese Systeme aus mehreren Agenten bestehen und dass das Verhalten des Gesamtsystems von dem Verhalten der einzelnen Agenten abhängt. • Beispiele: – Börse – Verkehr/Staus – Soziale Netzwerke, sich darin ausbreitende Informationen, Krankheiten – Klima, Globalisierung Graphen • Graphen sind mathematische Konstrukte, die aus einer Menge V={v1, v2, ..., vn} von Knoten (vertices) und einer Menge E={e1, e2, ...., em} (edges) von Kanten bestehen, wobei die Kantenmenge E ⊆VxV eine Teilmenge von VxV ist, also eine Relation (Beziehung). • Fragen in der realen Welt lassen sich oft sehr gut beantworten, wenn man die reale Welt durch einen Graphen approximiert (modelliert), die Frage auf dem Graphen löst, und die Antwort wieder auf die reale Welt überträgt. • Dazu geben wir im Folgenden ein paar Beispiele. Beispiele: Neuronale Netzwerke Author(s): (unknown) Institution: IBM & Ecole Polytechnique Federale de Lausanne Year: 2005 URL: http://tinyurl.com/ah3dd Project Description: IBM and Switzerland-based Ecole Polytechnique Federale de Lausanne are working on a joint initiative using the huge computational capacity of IBM's eServer Blue Gene supercomputer to create a detailed model of the circuitry in the neocortex - the largest and most complex part of the human brain, which is believed to be the center for higher cognitive functions. It is also the portion of the brain that evolved most recently and that is unique to mammals. By expanding the project to model other areas of the brain, scientists hope to eventually build an accurate, computer-based model of the entire brain. On its Web site, IBM notes that researchers can learn more about the morphology of the neocortex by inserting blue dye into each neuron. The neocortex is organized into six layers and thousands of columns. The first image shows a fraction of the cells and connections within the neocortex's microcircuitry. The second image illustrates the pyramidal neurons, which make up 80 percent of the neurons in the neocortex. © www.visualcomplexity.com, 2007 Beispiele: Straßennetzwerke Author(s): Sheep N. Dalton Institution: (unknown) Year: 2005 URL: http://www.thepurehands.org/fractional/ Project Description: These images are part of a research done by Sheep N. Dalton in the context of a paper for the Third Space Syntax conference in Atlanta, USA. The paper covers a new theory that can perform new kinds of configurational analysis. The software which performs the analysis is called "Meanda" (Mean Depth Angular) and was developed by Dalton. The project visualizes the network structure of the graph that is formed from the network of streets. This work is derived from a set of network theories in architecture known as "Space Syntax". It is generally found that these colors which are formed from a measurement of graph structure correlate well with observed patterns of pedestrian movement. The first image illustrates London Radius infinity Mean Depth. This is a vehicular map so Oxford street has been removed to represent it's non availability to cars. The second image shows Amsterdam Mean depth. © www.visualcomplexity.com, 2007 Beispiele: Metabolismus Author(s): (unknown) Institution: Roche Year: 2003 URL: http://www.expasy.ch/cgi-bin/show_thumbnails.pl Project Description: This highly complex diagram is part of Roche Applied Science "Biochemical Pathways" series of wall charts. Roche created two informative wall charts, representing metabolic research in many life science fields, including molecular biology, cell biology, receptors and immunological interactions, and much more. The charts make it easy to find the metabolic pathways that most interest a particular user/researcher. This digitized version of the pathways map focuses on Metabolic Pathways and is available at the ExPASy Molecular Biology Server. © www.visualcomplexity.com, 2007 Beispiele: Internet Author(s): Stephen G. Eick Institution: Bell Laboratories Year: 1993 URL: http://mappa.mundi.net/maps/maps_008/ Project Description: The arc map displays a 3D network structure as arcs curving smoothly above a flat map of the world. The data being visualized is Internet traffic flows between fifty countries, as measured by the NSFNET backbone in 1993. The colour, thickness and height of the arcs is used to encode the traffic statistics for particular inter-country links. The arcs are also partially translucent so as not to completely obscure lines at the back of the map, while their height above the base map is in relation to total volume of traffic flowing over a link. This has the effect of making the most important (high traffic) links, the highest and therefore most visually prominent on the map. The user has considerable interactive control over the arc map, for example the arc height scaling and translucency can be varied. The map can also be rotated and scaled, so that the user can view it from any angle. © www.visualcomplexity.com, 2007 Beispiele: Blogging Author(s): Lada Adamic, Natalie Glance Institution: HP Labs, Intelliseek Applied Research Center (Blogpulse.com) Year: 2005 URL: http://www.blogpulse.com/papers/2005/AdamicGlanceBlogWWW.pdf Project Description: In this paper, the authors studied the linking patterns and discussion topics of political bloggers. The goal was to measure the degree of interaction between liberal and conservative blogs, and to uncover any differences in the structure of the two communities. Specifically, they analyzed the posts of 40 "A-list" blogs over the period of two months preceding the U.S. Presidential Election of 2004, to study how often they referred to one another and to quantify the overlap in the topics they discussed, both within the liberal and conservative communities, and also across communities. They also studied a single day snapshot of over 1,000 political blogs. This snapshot captured blogrolls (the list of links to other blogs frequently found in sidebars), and presents a more static picture of a broader blogosphere. Most significantly, the authors found differences in the behavior of liberal and conservative blogs, with conservative blogs linking to each other more frequently and in a denser pattern. The image shown, portraits the community structure of the analyzed political blogs (expanded set), shown using utilizing a GEM layout in the GUESS visualization and analysis tool. The colors reflect political orientation, red for conservative, and blue for liberal. Orange links go from liberal to conservative, and purple ones from conservative to liberal. The size of each blog reflects the number of other blogs that link to it. © www.visualcomplexity.com, 2007 Die Welt als Graph • Der Beginn der Graphentheorie wird allgemein auf 1735 gelegt, als Leonhard Euler die folgende Frage beantwortete: Können die sieben Brücken über die Pregel in Königsberg 'in einem Zug', ohne eine mehrfach zu überqueren, überquert werden? Obwohl Euler selbst diese Frage noch nicht mit graphentheoretischen Methoden bearbeitete, markiert diese Arbeit den Beginn der Graphentheorie. Euler'sche Graphen – Oder: das ist das Haus vom Nikolaus • Graphentheoretisch kann man die Frage wie folgend modellieren: • Jeder Stadteil wird durch einen Knoten repärsentiert, • jede Brücke zwischen den Stadteilen wird als Kante zwischen den entsprechenden Knoten repräsentiert. • Fussnote: Man sollte nicht sagen, dass die Brücke eine Kante zwischen den Stadtteilen ist, denn dadurch wird die Modellebene ('Kante') mit der realen Welt ('Stadtteile') vermischt. Reale Welt Modell Im Modell: • wird ein Stadtteil nur noch durch einen Knoten repräsentiert, • es zählt nur noch, mit welchen Stadtteilen er verbunden ist, • alle weiteren Eigenschaften des Stadtteils (z.B. weitere Straßen, Größe, Name des Stadtteils) sind unerheblich! Pfade, Wege, Zyklen • Ein Weg W(v,w) ist eine geordnete Teilmenge, d.h., eine Folge von Kanten W(v,w)={e1, e2, ...., ek} aus E mit e1=(v, v1), ek=(vk-1, vk), und für alle 2 ≤ i ≤ k-1, ei=(vi-1, vi). Alle Elemente müssen aus E sein. • Ein Zykel ist ein Weg mit gleichem Anfangs- und Endknoten. • Ein Zykel ist ein einfacher Zykel, wenn kein Knoten mehrfach in ihm enthalten ist. Ein Zykel ist ein trivialer Zykel, wenn er (in einem ungerichteten Graphen) aus zweimal derselben Kante besteht. • Ein Pfad ist ein Weg, in dem kein Knoten mehr als einmal vorkommt. • Die Länge eines Pfades ist gleich der Anzahl der in ihm enthaltenen Kanten. • Ein Graph ist zusammenhängend, wenn es für alle Paare von Knoten einen sie verbindenden Pfad gibt. Reale Welt Modell Können die sieben Brücken über die Pregel in Königsberg 'in einem Zug', ohne eine mehrfach zu überqueren, überquert werden? Dieselbe Frage auf Modellebene: Können wir einen Pfad finden, der in demselben Knoten anfängt und endet und alle Kanten des Knotens genau einmal enthält? Die Antwort • Eine sogenannte 'Euler-Tour' ist genau dann möglich, wenn der Graph zusammenhängend ist und jeder Knoten eine gerade Anzahl von Kanten (Grad) hat. • Beweis: –Wir zeigen die Äquivalenz der folgenden Begriffe: 1. 2. 3. • Ein Graph hat eine Euler-Tour Alle Knoten des Graphen haben geraden Grad. Die Kanten von G können in (nicht-triviale) Zykel partitioniert werden. Zur Erinnerung: Eine Partitionierung zerlegt eine Menge M in Teilmengen M1, M2, ..., Mk, so dass die Vereinigung aller Teilmengen M ergibt und dass der Schnitt je zweier Teilmengen Mi, Mj, i != j leer ist. 1 Y2 Wenn es eine Euler-Tour gibt, und ein Knoten k-mal darin vorkommt, dann wird der Knoten über k Kanten betreten, und über k Kanten verlassen. Also hat der Knoten geraden Grad. 2Y3 • Per Induktion über die Anzahl der (einfachen) Zyklen k im Graphen – Induktionsanfang: Wenn es keinen Zykel im Graphen gibt, ist der Graph ein Baum. Da ein Baum mit mindestens einer Kante mindestens zwei Blätter hat (leicht zu sehen), darf der Baum keine Kante haben. Y Jeder Knoten hat Grad 0, der Baum lässt sich (definitionsgemäß) in Zykel partitionieren. – Induktionsschritt: Sei nun G ein Graph mit k>0 Zykeln. Sei nun C einer dieser (einfachen) Zykel, betrachte G-C (entferne die Kanten aus C in G). Dadurch hat jeder Knoten entweder denselben Grad wie vorher oder einen um zwei verminderten Grad. Der Graph hat einen Zykel weniger. Wegen Induktionsanfang ist damit der Beweis gemacht. (Überlegen Sie sich, was passiert, wenn durch das Wegnehmen des Zykels der Graph in Komponenten zerfällt!) 3Y1 • Sei nun G in die Zykel C= C1, C2, ..., Cm partitionierbar. • Weil G zusammenhängend ist, muss Zykel C1 mit mindestens einem Zykel C2 einen gemeinsamen Knoten haben. Vereinige diese beiden Zykel zu C', füge C' zu C zu, und lösche C1 und C2 aus C. Dabei wird keine Kante mehrfach gelaufen! • Nach m-1 dieser Vereinigungsschritte besteht C nur noch aus einem Zykel. ~ Welche Florentinische Familie war die wichtigste im alten Florenz? • Eine schon sehr alte Frage aus den Sozialwissenschaften ist: gegeben ein soziales System, wo ist der Dreh- und Angelpunkt des Systems, wer ist der/die Wichtigste? • Weil soziale Systeme komplex sind, konzentriert man sich auf eine Beziehung der sozialen Entitäten, z.B., wer hat wen gewählt, wer ist in welchem Aufsichtsrat, etc. Florentinischer Heiratsmarkt Kürzeste Pfade und Distanzen • Ein zusammenhängender Teilgraph G'=(V'⊆V, E') mit E' = {e=(v,w) aus E mit v, w in V'} ist eine Zusammenhangskomponente, wenn er nicht mehr erweitert werden kann, ohne dass der Zusammenhang verloren geht. • Ein kürzester Pfad P(v,w) zwischen v,w ist ein Pfad minimaler Länge. Wenn v und w in unterschiedlichen Zusammenhangs-komponenten liegen, gibt es keinen solchen Pfad. • Die Distanz d(v,w) zwischen v und w ist definiert als die Länge ihres kürzesten Pfades. Existiert dieser nicht, ist die Distanz definitionsgemäß ∞. • Der Durchmesser D(G) eines Graphen ist definiert als die maximale Distanz zweier Knoten in ihm. Zentralitätsmaße • Verschiedene Maße, z.B. – Grad (wer am meisten Kanten hat, ist der Wichtigste) – Stress Zentralität: Der ist am Wichtigsten, über den die meisten kürzesten Pfade laufen. – Exzentrizität: Der ist am Wichtigsten, der die kleinste maximale Distanz zu allen anderen Knoten im Graphen hat. – Nähe: Der ist am Wichtigsten, der die kleinste Gesamtdistanz zu allen anderen Knoten im Graphen hat. – ... Was ist die wichtigste Seite im Web? • Abhängig von der Suchanfrage. • Meistens erstmal textuelle Suche nach Seite mit entsprechenden Stichworten. • Dann Teilgraphaufbau, der die umgebenden Seiten mit einbezieht (bis zu einer bestimmten Distanz). • Dann z.B. 'Hubs and Authorities'-Analyse: – Wer zeigt auf die meisten anderen Seiten mit diesen Textbausteinen? Y Hub – Auf wen zeigen die meisten anderen Seiten? Y Authority Wer ist der am besten vernetzte Wissenschaftler? • Newman fragte sich 199X, welcher Wissenschaftler der am besten vernetzte Wissenschaftler ist. • Dazu untersuchte er das Co-AuthorshipNetzwerk des Online-Publikations-Archives ArXiv im Sektor condensed matter in Bezug auf: – meisten Publikation pro Autor; – meisten Co-Autoren per Autor; – größte Betweenness; etc. Wie können wir Proteine annotieren? • Proteine sind biologische Substanzen mit einer bestimmten Funktion, die teilweise von ihrer Struktur und ihren Wechselwirkungen mit anderen Proteinen abgeleitet werden kann. • Protein-Protein-Interaktionsnetzwerke repräsentieren diese Wechselwirkungen. • Durch das Auffinden von dichten Teilgraphen (sog. Clustern) können Gruppen von Proteinen gefunden werden, die oft auch dieselbe Funktion in der Zelle haben. Wenn ein neues Protein in eine solche Gruppe fällt, können gezielte Experimente schnell zeigen, ob das neue Protein auch dieselbe Zellfunktion hat. Wie gut sind die AmazonEmpfehlungen? • Auf jeder Seite von Amazon findet man Links unter dem Titel 'Kunden, die diesen Artikel kauften, kauften auch'. • Diese Links kann man crawlen und die entstehenden Netze analysieren. Wie empfindlich sind soziale Netzwerke gegenüber Krankheiten? • Wie können wir soziale Netzwerke modellieren? • Erste Idee: Zufallsgraphen, jede Kante hat dieselbe Wahrscheinlichkeit p zu existieren. • Ein solcher Graph hat für p ~ log n/n einen Durchmesser von O(log n). • Die Wahrscheinlichkeit, dass zwei Nachbarn eines Knoten auch untereinander eine Kante haben, ist p. Small-Worlds • Diese Wahrscheinlichkeit ist in sozialen Netzwerken viel höher als die Wahrscheinlichkeit, dass wir irgendeinen Menschen 'weit weg' von uns kennen. (Gemeinsame Parties, Kollegen, Sportvereine). • In sozialen Netzwerken ist also der sogenannte Clusteringkoeffizient viel höher. • Clusteringkoeffizient ist definiert als: Small-World-Model • Um ein Netzwerk als Zufallsgraphen zu modellieren, sollte die Anzahl der Knoten gleich sein, die der erwarteten Kanten gleich der Anzahl der echten Kanten. • Also: – Bernoulli-Experiment für jede Kante, – erwartete Anzahl von Kanten – Für gegebenes n und m p ausrechnen Reale Netzwerke • Wenn p nach der auf der vorangehenden Folie ausgerechnet wird, haben reale Netzwerke einen viel höheren (bis zu 1000 mal höheren) Clusteringkoeffizienten als der entsprechende Zufallsgraph. – Beachte: Clusteringkoeffizient in Zufallsgraphen ist gleich p! • Neues Modell ist notwendig! Realwelt-Modelle • Ein anderes Autorenteam konnte zeigen, dass Zufallsgraphen auch in einem anderen Aspekt reale Netzwerke nicht gut modellieren. • Gradverteilung: Zeichnet man die Wahrscheinlichkeit P(k), einen Knoten mit Grad k aus V zu ziehen gegen den Grad k auf, dann ergibt sich bei Zufallsgraphen eine Normalverteilung. • Bei den meisten realen Netzwerken findet man eine Verteilung P(k) ~ k-γ, sog. Power-Law oder skalenfreie Gradverteilung. Realwelt-Modelle • Es gibt viele neue Realwelt-Modelle. • Diese dienen z.B. dazu, die Verbreitung von – Informationen in P2P-Netzwerken, – Krankheiten in sozialen Netzwerken, – kaskadierenden Ausfällen von Energiekraft-werken in Stromversorgungsnetzwerken zu analysieren. • Man kann mit ihnen aber auch versuchen, – Wichtige Aktivierungsmuster in Genaktivationsnetzwerken zu finden; – die wichtigsten metabolischen Moleküle zu bestimmen; – die Empfindlichkeit von Netzwerken auf verschiedene Arten von Attacken zu testen: es stellt sich heraus, dass Zufallsgraphen weniger anfällig für (terroristische) Attacken sind, während skalenfreie Netzwerke weniger anfällig für zufällige Ausfälle sind. Vorlesungsstoff • Wir werden basale Algorithmen vorstellen, auf denen diese und andere Algorithmen aufbauen (z.B. in Algorithmen und Komplexität, Bioinformatik) • Insbesondere: – – – – Topologische Sortierung Berechnung kürzester Wege, Minimaler Spannbaum/Single Linkage Clustering, Graph-Durchwanderungsalgorithmen (DFS/BFS) Lehrziele der Vorlesung Algorithmen • Sehen Sie diese Folien als Motivation für die Lehrziele in Algorithmen: – Überblick über grundlegende Algorithmen und Datenstrukturen; • – Ohne diese grundlegenden Algorithmen, z.B. die Breitensuche in Graphen können die vorgestellen Fragestellungen nicht beantwortet werden. Einüben und Verstehen der theoretischen Analyse von Algorithmen; • Nur mit Hilfe der theoretischen Analyse von Algorithmen können wir effiziente von nicht-effizienten Algorithmen unterscheiden. Bei Netzwerkgrößen von bis zu 500 Millionen Knoten durchaus wichtig! Lehrziele der Vorlesung Algorithmen – Formalisierung von algorithmischen Fragestellungen; • Die an uns herangetragenen Fragestellungen sind oft unklar und schwammig. Erst eine Formalisierung erlaubt einem Team von Programmierern eine Lösung zu finden, da dadurch die Mehrdeutigkeit natürlichsprachlicher Formulierungen verringert wird. – Design von effizienten Algorithmen und Datenstrukturen; • Die Kenntnis einer breitgefächerten Sammlung von Algorithmen und Datenstrukturen befähigt Sie dazu, neue Lösungen für neue Probleme zu finden, oder eine Verbindung zwischen zwei Problemen zu finden, so dass sie das zweite mit Verfahren des ersten lösen können. – Verbesserung der Programmierfähigkeiten. • Selbst ein effizienter Algorithmus ist nur so gut wie seine Implementierung! In eigener Sache Studienarbeiter und Bachelorstudenten sind zu diesen Themen (und anderen, wie Graphenzeichnen und jeglichem algorithmischen Themengebiet) jederzeit herzlich bei uns willkommen! ☺ 7.4 Kürzeste und Billigste Wege in Graphen Sei G = (V, E) ein gerichteter Graph mit Kostenfunktion c: E R. Sei p = ((v0,v1) ..., (vk-1, vk) Pfad von v0 = v nach vk = w. Dann heißt k −1 c( p ) := ∑ c(vi , vi +1 ) i =0 Kosten des Pfades von v nach w. dist(v, w) = inf {c(p) | p ist Pfad von v nach w} heißt Entfernung oder Distanz zwischen v und w. 1 Beispiel: 2 –1 4 1 2 3 3 4 dist(1,2) = dist(3,1) = dist(4,6) = 2 6 –6 5 3 2 Beispiel: 2 –1 4 1 2 3 3 4 dist(1,2) = – 1 dist(3,1) = ∞ dist(4,6) = – ∞ , da Pfad (4,(5,6,4)i) Kosten 3 – i hat für alle i ≥ 0. Kantenfolge!! negativer Zyklus 2 6 –6 5 3 3 3 grundsätzliche Problemstellungen: 1 single pair shortest paths (kürzeste Wege zwischen einem Knotenpaar) 2 single source shortest paths (kürzeste Wege von einem Knoten aus) 3 all pairs shortest paths (kürzeste Wege zwischen allen Knotenpaaren) 4 7.3.1 Single Source Shortest Paths Sei s der „Quellknoten“. G = (V, E). Gesucht ist für alle Knoten u ∈V ∞, falls kein Pfad von s nach u in G, dist (s, u ), sonst. δ (u ) := Lemma: Sei u ∈ V . Dann gilt: i) δ (u ) = −∞ gdw. u ist erreichbar aus negativem Zyklus, der von s aus erreichbar ist. ii) δ (u ) ∈ R ⇒ ∃ billigster einfacher Pfad von s nach u mit Kosten δ (u ) . 5 Beweis: i) "⇐" : klar! "⇒" : 6 ii) 7 7.4.1.1 Der geg. Graph G = (V, E) ist azyklisch. Bsp.: 2 2 3 5 2 5 s –1 3 2 6 4 1 δ(s) = δ(2) = δ(3) = δ(4) = δ(5) = δ(6) = 8 Nachfolgender Algorithmus setzt voraus, dass der Graph topologisch sortiert ist, „also“ V = {1, 2, ... , n}. Algorithmus: d(s) 0; //d von s nach s ist 0 Pfad(s) Ø; //Pfad wird für jeden Knoten als Liste implementiert for all v ∈ V \ {s} do d(v) //Initialisierung ∞ od; for v = s + 1 to n do //für alle 'Nachfolger' von s d(v) min {d(u) + c(u,v) | (u,v) ∈ E} //setze d(v) //auf minimalen Wert //Sei u* der Vorgänger mit Pfad(v) Pfad(u*) ° (u,v) //d(u*)+c(u*,v) minimal od 9 Satz: Nach Ausführung von obigem Algorithmus gilt ∀v ∈ V 1) d (v ) = δ (v ) 2) d (v ) < ∞ ⇒ Pfad(v) ist billigster Weg von s nach v. 3) Algorithmus hat Laufzeit O( n + m), wobei n = |V| und m = |E|. Beweis: Korrektheit ( 1) + 2) ): Induktion über v v < s: δ (v ) = ∞, da v von s aus nicht erreichbar. d (v ) = ∞, da nach Init. nicht mehr verändert v = s: δ (v ) = d (v ) = 0. Pfad(s) = s ist einziger Weg. 10 v > s: 11 Laufzeit: Topologisches Sortieren kostet Zeit O( n + m ). Initialisierung: O(n) „Hauptteil“: Komplexität wird im Wesentlichen bestimmt durch ∑ In(v ) = m , v∈V also O( n + m ). 12 7.4.1.2 Alle Kantenkosten nichtnegativ (d.h. ∀e ∈ E , c(e) ≥ 0), Zyklen erlaubt Dijkstras Algorithmus: Anmerkung: Nachfolgend werden nur Distanzen berechnet; eigentliche Pfadberechnung analog zu vorherigem Algorithmus. Idee: Für Menge S von Knoten mit bereits bestimmtem kürzesten Pfad muss es einen Knoten x ∈ V \ S geben, so dass der kürzeste Weg von s nach x über die Knoten in S geht und dann noch eine Kante aus S nach x benutzt. Genauer: x ist der Knoten, der für alle u ∈ S den Ausdruck δ (u ) + c (u , x ) minimiert. 13 Nachfolgend: S := Menge der Knoten u mit bekanntem δ(u ). S ′ := Menge der Knoten aus V \ S , die Nachbarn in S haben Algorithmus: S ← {s}; d (s ) ← 0; S ′ ← OutAdj(s ); for all u ∈ S ′ do; //Initialisiere S, d(s) //S' entspricht am Anfang den Nachbarn von s //für diese initialisiere d'(u) d ' (u ) ← c(s, u ); for all u ∈ V − (S ∪ S ′) do //für die anderen setze es auf +∞ d ' (u ) ← ∞; 14 while S ′ ≠ o/ do x ← derjenige Knoten aus S ′ mit minimalem d'-Wert; d ( x ) ← d ' ( x ); S ← S ∪ {x}; S ′ ← S ′ \ {x}; for all u ∈ OutAdj ( x ) do if u ∉ S then S ′ ← S ′ ∪ {u }; d ' (u ) ← min {d ' (u ), d ( x ) + c ( x , u )} fi od od 15 Beispiel (Dijkstra, Ungerichteter Graph): S= S´= d(s) = d´(C) = d´(D) = d´(E) = S= S´= d(D) = d´(C) = d´(E) = d´(B) = 3 A 1 B 4 1 4 2 C 3 D 1 2 1 2 0 s 5 E 3 S= S´= d(E) = d´(B) = S= S´= d(C) = d´(B) = d´(E) = d´(A) = S= S´= d(A) = d´(B) = d´(E) = S= d(B) = 16 Beispiel (Dijkstra, Ungerichteter Graph): S = {s, D, C}, S´={E, B, A} d(C) = 2, d´(B) = 5, d´(E) = 3, d´(A) = 3 S = {s}, S´={C, D, E} 3 1 A B 4 d(s) = 0, d´(C) = 2, 1 4 d´(D) = 1, 3 D 1 2 C d´(E) = 5 1 2 2 S = {s, D, C, A}, S = {s, D}, S´={E, B} 0 s E 3 5 S´={C, E, B} d(A) = 3 d(D) = 1, d´(B) = 4, S = {s, D, C, A, E}, d´(C) = 2, d´(E) = 3 S´={B} d´(E) = 3, d(E) = 3 S = {s, D, C, A, E, B}, d´(B) = 5 d´(B) = 4, d(B) = 4, 17 Beispiel (Dijkstra, Gerichteter Graph): 1 1 10 4 5 4 46 B 0 s 1 H 1 F 3 7 7 5 2 2 C 2 2 3 3 3 6 3 G 5 2 A J 5 1 E 1 1 2 3 2 I D 7 10 1 K 8 84 5 5 9 1 10 2 L ∞ 10 9 9 12 11 11 9 18 Lemma: Sei x ∈ S ′ so gewählt, dass d ' (x ) minimal ist. Dann gilt d ' (x) = d (x). Beweis: Sei p ein billigster Weg von s nach x derart, dass alle Kanten bis auf die letzte zwischen Knoten aus S sind. Angenommen, es gäbe einen billigeren Weg q von s nach x. q muss einen ersten Knoten v in V \ S haben und nach Wahl von x ist d ′(v ) ≥ d ′( x ). Da alle Kanten nichtnegativ sind, gilt aber c(q ) ≥ d ' (v ) ≥ d ' (x ) = c( p ), ein Widerspruch zu der Aussage, dass q ein billigerer Weg als p sei. 19 Zur Laufzeit: Implementiere S und S ′ als Bitvektoren und d und d' als Arrays. •Aufwand für „ for all u ∈ OutAdj( x ) “: ∑ OutAdj( x ) = O(m + n ). x∈V •n-maliges Minimieren über S ′ : O(n ² ). Insgesamt ergibt sich Laufzeit O(n²+m), für dichte Graphen (m ≈ n ² ) ausreichend. 20 Alternativ: Speichere S ′ in Heap (balancierter Baum), gemäß d' -Werten geordnet. Damit Laufzeit O((m+n)log n),da nun Einfügen und Löschen in S ′ je O(log n ) Zeit kostet. Für dünne Graphen ist dies eine Verbesserung. 21 Satz: Mit Hilfe des Algorithmus von Dijkstra läßt sich das Single Source Shortest Path Problem in Laufzeiten O(n² ) bzw. O((m + n ) log n ) lösen. Mitteilung: Mit Hilfe sogenannter Fibonacci-Heaps läßt sich die Laufzeit des Algorithmus von Dijkstra asymptotisch zu O(n log n + m) verbessern. 22 7.4.1.3 Negative Kantenkosten erlaubt, aber keine negativen Zyklen. Bellman-Ford-Algorithmus Das „Entspannen einer Kante (v,w)“ bezeichnen wir mit Relax ((v,w)): δ (w) ← min{δ (w), δ (v ) + c(v, w)} Offensichtlich: Das Entspannen einer Kante kann den δ-Wert niemals erhöhen. 23 Lemma: Falls für alle w∈ V vor dem Entspannen einer beliebigen Kante (v,w) gilt, dass d(w) ≥δ(w) , dann gilt dies auch hinterher. Beweis: (Induktion über Kanten) Jeder Weg von s nach v zusammen mit Kante (v,w) ergibt Weg von s nach w. Deshalb δ (v ) + c (v, w ) ≥ δ (w ). Weiterhin: δ (v) + c(v,w) ≤ d (v) + c(v,w) ⇒ δ (w) ≤ d (v ) + c(v, w) ⇒ δ (w) ≤ d (w) falls (v,w) „echt“ relaxiert. 24 Algorithmus: d (s ) ← 0; for all v ≠ s do d (v ) ← ∞ while „ möglich“ do Entspanne Kanten od Beobachtung: d(u) wird immer kleiner, unterschreitet aber nie δ(u) . 25 Offen bleibt noch die Frage nach der „Entspannungs-Reihenfolge“, sodass d schnell gegen δ konvergiert. Lemma: Sei w ∈ V und sei δ (w) < ∞ und sei (v, w) die letzte Kante auf dem billigsten Weg von s nach w. Dann gilt: Falls (v,w) entspannt wird, nachdem d(v) = δ(v) geworden ist, so ist danach d(w) = δ(w). Beweis: nach Definition der Kante (v,w). 26 Algorithmus: d (s ) ← 0; for all v ≠ s do d (v ) ← ∞; for i ← 1 to n − 1 do for all (v, w) ∈ E do Relax ((v, w)); Bem.: im Beispiel spielt die Reihenfolge der Kanten in der for all-Schleife eine Rolle, die Aussagen gelten aber für alle Reihenfolgen Lemma: Für i=0,..., n-1 gilt: Nach dem i-ten Durchlauf der for-Schleife ist d (w) = δ(w) für alle w∈ V , für die es einen billigsten Pfad mit i Kanten von s nach w gibt. 27 Beweis: Induktion über i: i = 0 : d (s ) = δ(s ), da es keine negativen Zykel gibt. i → i +1: Sei w der Knoten mit billigstem Weg von s nach w mit i+1 Kanten und sei (v,w) die letzte Kante auf diesem Weg. Also gibt es billigsten Weg von s nach v der Länge i und nach Induktionsannahme ist nach dem i-ten Durchlauf der for-Schleife d (v ) = δ(v ) . Im (i+1)-ten Durchlauf wird insbesondere (v,w) entspannt: ⇒ d (w) = d (v ) + c((v, w)) = δ(v ) + c((v, w)) = δ( w ) 28 Korollar: Nach dem (n-1)-ten Schleifendurchlauf gilt für alle v aus V, dass d (v ) = δ(v ). Beweis: Nach obigem Lemma und der Annahme der Nichtexistenz negativer Zyklen folgt, dass alle billigsten Wege aus höchstens n-1 Kanten bestehen. Insgesamt: Satz: In Laufzeit O(n ⋅ m ) läßt sich das Single Source Shortest Path Problem lösen, falls der Graph keine negativen Zyklen enthält. 29 Beispiel (Bellmann-Ford, Gerichteter Graph, positive Kanten): 1 B 10 5 4 6 0 s 1 H 1 F 3 7 2 C 2 5 2 L ∞ 2 6 G 3 3 3 3 5 2 A J 5 1 E 1 1 10 9 2 3 I D 7 10 1 4 K 12 11 8 5 9 1 4 i = 1, i = 2, i = 3, i = 4, i = 5, i = 6, i = 7, i = 8, 30 Beispiel (Bellmann-Ford, Gerichteter Graph, negative Kanten, aber keine negativen Zyklen): 1 7 1 4 5 1 B 6 0 s 1 H 1 F 3 4 -1 C 2 -5 2 L ∞ 2 6 G -3 -3 3 3 5 2 A J -5 1 E -1 1 1 2 3 I D 7 7 1 4 K 3 5 5 4 4 i = 1, i = 2, i = 3, i = 4, i = 5, i = 6, i = 7, i = 8, 31 Beispiel (Bellmann-Ford, Ungerichteter Graph, positive Kanten): 1 0 1 A 6 5 6 6 2 s 2 4 B 4 3 C 5 1 D 12 11 10 E 4 9 7 6 i = 1, i = 2, i = 3, i = 4, i = 5, Warum braucht der Algorithmus hier n-1 Iterationen bis zur Konvergenz? Welches wäre der einfachste Graph mit dieser Eigenschaft? 32 7.4.1.4: Graphen mit negativen Zyklen Algorithmus: 1 2 Führe zuerst die n-1 for-Schleifendurchläufe des Algorithmus von Bellman-Ford aus und merke in d1 die so erzielten d-Werte. Führe n weitere for-Schleifendurchläufe aus und speichere das Ergebnis in d2. A1: Wie kann man nun erkennen, ob ein Knoten durch einen negativen Zyklus mit s verbunden ist? 33 Lemma: Für w ∈ V gilt : i) d 2 (w) = d1 (w) ⇒ δ(w ) = d1 (w) ii) d 2 (w) < d1 (w) ⇒ δ(w ) = −∞ Beweis: 34 Bemerkung: Wenn sich nach nur einem weiteren Durchlauf der for-Schleife nach Abschluss von 1 noch etwas ändert, so gibt es einen negativen Zyklus, sonst aber nicht. Um aber alle Knoten entdecken zu können, die durch einen negativen Zykel von s aus erreichbar sind, benötigt man in 2 insgesamt n Durchläufe. A2: Geben Sie eine Graphenfamilie an, bei der das so ist. 35 7.4.2 All Pairs Shortest Paths (Floyd-Warshall) Annahme: Keine negativen Zyklen. V = {1,2,..., n} Für i, j ∈ V und 0 ≤ k ≤ n definiere δ k (i, j ) := Kosten des billigsten Weges von i nach j , dessen innere Knoten ≤ k sind. Beispiel: δ 0 (1,4 ) = δ1 (1,4) = δ 2 (1,4) = δ 3 (1,4 ) = δ 4 (1,4) = 4 2 2 2 1 -3 3 4 5 A3: Füllen Sie die Werte links für den obenstehenden Graphen aus. 36 Also: c(i, j ), falls (i, j ) ∈ E. δ 0 (i, j ) = 0 , falls i = j. ∞ , sonst. A4: Wann haben wir die Kosten für den billigsten Weg von i nach j garantiert berechnet? A5: Wie berechnet sich δ k aus δ k-1 ? 37 A4: Wann haben wir die Kosten für den billigsten Weg von i nach j garantiert berechnet? δ n (i, j ) = δ(i, j ) = Kosten des billigsten Weges von i nach j , da nach n Schritten (ohne negative Zyklen) jeder Knoten, der von s aus erreichbar ist, auf seinem billigsten Weg erreicht wurde. A5: Wie berechnet sich δ k aus δ k-1 ? 2 Möglichkeiten: Entweder k Teil eines billigeren Weges oder nicht: δ k-1 (i, j ) i δ k-1 (i, k ) k j δ k-1 (k , j ) Also gilt: δ k (i, j ) = min{δ k −1 (i, j ), δ k −1 (i, k ) + δ k −1 (k , j )} 38 Beispiel (Floyd-Warshall, Ungerichteter Graph, positive Kanten): 6 2 1 6 2 1 2 4 3 4 1 4 5 5 D0= 6 D1 = D0 D2= D3= D4= u.s.w. 39 Beispiel (Floyd-Warshall, Ungerichteter Graph, positive Kanten): 6 2 1 6 2 1 2 4 D1 = D0 0 1 1 0 D2= 3 2 7 6 u u u u 3 3 2 0 2 5 u 7 6 2 0 1 6 u u 5 1 0 4 5 u u u 6 4 0 4 2 0 2 5 u u 6 2 0 1 6 u u 5 1 0 4 u u u 6 4 0 0 1 3 1 0 2 D4= 3 2 0 5 4 2 6 5 3 11 10 8 5 4 2 0 1 6 6 5 3 1 0 4 11 10 8 6 4 0 4 1 4 5 0 1 D3= 3 5 8 u D0= 6 1 0 2 4 7 u 3 2 0 2 5 u 5 4 2 0 1 6 8 7 5 1 0 4 u u u 6 4 0 0 1 4 u u u 1 0 2 6 u u u.s.w. 40 Algorithmus (Floyd-Warshall): for all i , j ∈ V do A6: Geben Sie den Algorithmus an, der δn(i,j) für alle i,j berechnet. c (i , j ), falls (i , j ) ∈ E δ 0 (i , j ) ← 0 , falls i = j ∞ , sonst ; od A7: Welche Laufzeit hat der Algorithmus? 41 Algorithmus (Floyd-Warshall): for all i , j ∈ V do c (i , j ), falls (i , j ) ∈ E δ 0 (i , j ) ← 0 , falls i = j ∞ , sonst ; od for k = 1 to n do for i = 1 to n do for j = 1 to n do δ k (i , j ) ← min { δ k −1 (i , j ), δ k −1 (i , k ) + δ k −1 (k , j )} od od od A7: Laufzeit: O(n³) 42 Alternativer Algorithmus: Benutze Algorithmus von Dijkstra bzw. Bellman-Ford Idee: Führe n-mal für alle möglichen Startknoten Bellman-FordAlgorithmus aus. Laufzeit : O(n ⋅ nm ) Beobachtung: Es reicht sogar, einmal Bellman-Ford und (n-1)-mal Dijkstra auszuführen. Laufzeit : O(n ⋅ (n + m ) log n ) 43 7.4.3 All Pairs Shortest Paths (Matrix-Form) Annahme: Keine negativen Zyklen. G = (V , E ), V = {1,2,..., n} Lemma: Wenn ein Pfad von i über k nach j der kürzeste Pfad von i nach j ist, dann sind auch die Pfade von i nach k und von k nach j jeweils kürzeste Pfade. A8: Beweis 44 Sei G als Adjazenzmatrix W = (w((i,j))) repräsentiert. Sei p ein kürzester Pfad von i nach j mit m Kanten. Es gilt: m < n. Falls i = j, dann hat p die Länge 0 und keine Kanten. Falls i ≠ j, dann zerlegen wir p in Pfad p´ von i nach k und Kante (k, j), wobei p´ dann m-1 Kanten hat. Es gilt: δ (i, j ) = δ (i, k ) + wkj 45 Sei lij(m ) der kürzeste Pfad zwischen Knoten i und j, der höchstens m Kanten hat. Für m = 0 gilt (0) ij l 0 falls i = j = sonst ∞ Für m ≥ 1 berechnen wir ( { +w } lij( m ) = min lij( m −1) , min1≤ k ≤ n lik( m −1) + wkj { = min1≤ k ≤ n lik( m −1) }) denn wjj = 0 für alle j kj Da ein kürzester Weg von i nach j höchstens n-1 Kanten hat, gilt δ (i, j ) = lij( n −1) = lij( n ) = lij( n +1) = ... 46 (m ) Wir berechnen also die lij in einer Folge von Matrizen L(1), L(2), ... L(n-1), mit L(m) = ( lij(m ) ). Algorithmus Slow-All-Pairs-Shortest-Paths (W) 1) L(olc) := W Zeitaufwand: O(n4) 2) for m := 2 to n-1 do begin 3) for i := 1 to n do Verwendung von 4) for j := 1 to n do begin nur zwei Matrizen 5) lij(new) := ∞ L(new) und L(old) 6) for k := 1 to n do 7) lij( new) := min(lij( new) , lik( old ) + wkj ) 8) end 9) L(old) := L(new) 10) end 4 Beispiel: 2 2 1 -3 0 4 − 3 ∞ ∞ 0 ∞ 2 (1) L = ∞ 2 0 5 ∞ ∞ ∞ 0 0 − 1 − 3 1 ∞ 0 ∞ 2 ( 3) L = ∞ 2 0 4 ∞ ∞ ∞ 0 2 L( 2 ) 3 47 4 5 0 −1 − 3 ∞ 0 ∞ = ∞ 2 0 ∞ ∞ ∞ 2 2 4 0 Bemerkung: Laut Cormen et al. sieht dieser Algorithmus ein bisschen aus wie der Algorithmus zur Matrixmultiplikation und wird daher von den Autoren auch 'Matrixmultiplikaitonsalgorithmus' genannt. Übung: Matrixmultiplikationsalgorithmus aufschreiben!! 48 Beispiel (Matrix"multiplikations"algorithmus): 2 1 6 2 4 0 1 3 1 0 2 L2 = 3 2 0 6 4 2 9 7 3 u 12 8 3 6 4 2 0 1 5 9 u 7 12 3 8 1 5 0 4 4 0 4 6 2 1 5 0 1 L1=W= 4 u u u 1 5 6 4 0 1 3 1 0 2 L3 = 3 2 0 5 4 2 7 5 3 1210 7 5 4 2 0 1 5 7 12 5 10 3 7 1 5 0 4 4 0 1 0 2 6 u u 4 2 0 2 5 u u 6 2 0 1 6 u u 5 1 0 4 u u u 6 4 0 0 1 1 0 L4 = 3 2 5 4 6 5 11 9 5 L : 10 3 2 0 2 3 7 5 4 2 0 1 5 6 11 10 5 9 3 7 1 5 0 4 4 0 49 Geht es auch schneller? Ja, durch Quadrierung! Bisher hatten wir berechnet: L(1) = L(0) * W = W L(2) = L(1) * W = W2 L(3) = L(2) * W = W3 ... L(n-1) = L(n-2) * W= Wn-1 Jetzt berechnen wir schneller: L(1) = =W L(2) = W * W = W2 L(4) = W2 * W2 = W4 L(8) = W4 * W4 = W8 ... 50 Algorithmus Fast-All-Pairs-Shortest-Paths (W) 1) L(old) := W 2) m := 1 Zeitaufwand: O(n3 log n), 3) while m < n-1 do das ist aber immer noch 4) for i := 1 to n do langsamer als der 5) for j := 1 to n do begin Floyd-Warshall-Algorithm. lij(new) := ∞ 6) 7) for k := 1 to n do lij( new) := min(lij( new) , lik( old ) + lkj( old ) ) 8) 9) end 10) m := 2m 11) L(old) := L(new) 12) endwhile 51 Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 8 8 HASHING Hashing In einem Universum U = [0, . . . , N − 1] sei S ⊆ U die zu verwaltende Menge. Zur Verfügung stehen sollen die Operationen “Zugriff ”, “Einfügen” und “Streichen”. Hashtafel: T [0, . . . , m − 1] Hashfunktion: h : U → [0, . . . , m − 1], a → T [h(a)] Beispiel: N = 50, m = 3, S = {2, 21} h(x) = x mod 3 21 0 1 2 2 Problem: Kollisionen, also h(x) = h(y) für x 6= y, z.B. x = 2, y = 20 Ziel: kleine Hashtafel, wenig Kollisionen 8.1 Hashing mit Verkettung (Chaining) Die i-te Liste in Tafel T (0 ≤ i ≤ m − 1) enthält alle x ∈ S mit h(x) = i 0 1 2 → 2 → 21 Laufzeit: worst-case: O(n) (alle Elemente in einer Liste), aber im Mittel viel besser. Wahrscheinlichkeitsannahmen: 1. h(x) kann in O(1) ausgewertet werden | 2. h−1 (i) = |U m für alle i = 0, . . . , m − 1 (gleichmäßige Verteilung der Zahl der Einträge) h soll Einträge von U gleichmäßig verteilen 3. Für eine Folge von n Operationen gilt: Wahrscheinlichkeit, dass j-tes Element der Folge ein festes x ∈ U ist, ist N1 . ⇒ Operationen sind unabhängig und gleichverteilt. ⇒ Sei xk Argument der k-ten Operation 1 prob(h(xk ) = i) = m d.h. auch Funktionswerte sind gleichverteilt. 85 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 8 HASHING Definition: ( 1 , falls x 6= y, h(x) = h(y) δh (x, y) = 0 , sonst X δh (x, S) = δh (x, y) (Zahl der Element in T (h(x))) y∈S ⇒ Kosten von Operation Zugrif f (x) = 1 + δh (x, S) Satz: Mittlere Kosten von Zugrif f (x) sind 1 + n m = 1 + β (β . . . Belegungsfaktor) Beweis: Sei h(x) = i und pik die Wahrscheinlichkeit, dass Liste i genau k Elemente enthält, dann ist n 1 n−k 1 k · 1− m . Erwartungswert der Kosten des Zugrif f (x) ist: pik = · m k n−k X n 1 k X X 1 · 1− k· · pik (1 + k) = pik + k m m | {z } k≥0 =1 Da k · n k =n· n−1 k−1 , gilt = = k−1 n−k n X n−1 1 1 · 1− · · k−1 m m m n−1 n 1 1 n 1+ · + 1− =1+ m m m m 1+ Wie groß sollte β sein? β ≤ 1: Zugriff erwartet in O(1) β ≤ 41 : Platz für Hashtafel und Listen ist O(n + m) = O(n + nβ ) ≈ O(n) für geeignet große β, z.B. β = 1 2 Beachte: Durch Einfügen und Streichen kann β schnell zu groß bzw. zu klein werden. ⇒ Rehashing. ⇒ Folge von Hashtafeln T0 , T1 , . . . der Größe m, 2m, 4m, . . . (Ti hat die Größe 2i · m) ⇒ Falls β = 1: Umspeichern in Ti+1 ⇒ β = 1 2 ⇒ Falls β = 14 : Umspeichern nach Ti−1 ⇒ β = 1 2 Technik: Amortisierte Analyse zeigt, dass n Operationen erwartet in O(n) gehen. Alternative: Offene Adressierung. Nur 1 Element pro Tafeleintrag. Folge von Hashfunktionen. Ist 1. Eintrag belegt, probiere zweiten. h1 , h2 , . . . z.B. hi (x) = (h(x) + 1) mod m (linear probing) oder hi (x) = h(x) + c1 · i + c2 · i2 mod m (quadratic probing) 8.2 Perfektes Hashing Die Hashfunktion soll injektiv sein. Sei S bekannt. Stufe 1: Hashing mit Verkettung ⇒ Listen Stufe 2: Für jede Liste eine eigene injektive Hashfunktion. 86 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 8 HASHING Wahl einer injektiven Hashfunktion: Sei U = {0, . . . , N − 1}, hk : {0, . . . , N − 1} → {0, . . . , m − 1}, k ∈ {1, . . . , N − 1} mit hk (x) = ((k · x) mod N ) mod m. Sei S bekannt. Wähle k mit hk injektiv. Wir messen Injektivität wie folgt: bik = |{x ∈ S|hk (x) = i}| für 1 ≤ k ≤ N − 1, 0 < i ≤ m − 1 also bestimmen wir die Zahl der x ∈ S mit gleichem Hashwert i für hk . Dann ist bik (bik − 1) = (x, y) ∈ S 2 |x 6= y, hk (x) = hk (y) = i , also die Zahl der Paare, die Injektivität verletzen. Pm−1 Sei Bk = i=0 bik (bik − 1). Falls Bk < 2, dann ist hk |S injektiv. Pm−1 n n für alle i = 0, . . . , m − 1, dann gilt: Bk = i=0 m · Ist bik = m n m −1 −n· n m −1 Lemma: Mit obigen Vorraussetzungen und einer Primzahl N gilt: N −1 m−1 X X bik · (bik − 1) ≤ 2 · k=1 i=0 n(n − 1) · (N − 1) m Beweis: siehe Literatur. Bemerkung: Die mittlere Zahl der Kollisionen ist 2 · n(n−1) . m ⇒ Bei m > n(n − 1) ist die mittlere Anzahl von Kollisionen < 2. ⇒ Es existieren hk , hk|s injektiv. Korollar: Mit obiger Voraussetzung gilt: 1. Es existiert ein k ∈ [1, . . . , N − 1], so dass Bk ≤ 2 · o n und |A| > N 2−1 . 2. Sei A = k|Bk > 4 · n(n−1) m ⇒ P k≥1 Bk ≥ P ⇒ Mindestens k∈A N −1 2 Bk > N −1 2 · 4(n−1)n m hk haben Bk ≤ =2· 4n(n−1) m (n−1)n m n(n−1) . m (N − 1) Widerspruch! Korollar: Mit obigen Voraussetzungen gilt: 1. Ein k ∈ [1, . . . , N − 1] mit Bk ≤2 · n(n−1) m kann in Zeit O(m + N n) bestimmt werden. 2. Sei m = n(n − 1) + 1, dann gibt es ein k mit hk|S injektiv und k kann in Zeit O(n2 + nN ) gefunden werden. 3. Sei m = 2n(n − 1) + 1, dann ist die Hälfte der hk|S injektiv. Besimmungszeit ist ransomisiert O(n2 ). 4. Ein k ∈ [1, . . . , N − 1] mit Bk ≤ 4 · n(n−1) m kann randomisiert in Zeit O(m + n) gefunden werden. 5. Sei m = n. Ein k mit Bk ≤ 2(n − 1) kann in Zeit O(nN ) gefunden werden. 6. Sei m = n. Randomisiert kann ein k mit Bk ≤ 4 · (n − 1) in O(n) gefunden werden. 87 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 8 HASHING Realisierung: √ 1. Bei m = n gibt es ein hk , so dass die Länge der Listen O( n), da Bk maximal das Quadrat der Längen und Bk < 4n ist. 2. Wende auf jede Teilmenge mehrmals Hashing an. Sei |S| = n = m. (a) Sei k gewählt, so dass Bk ≤ 4(n − 1) < 4n. (b) Sei wi = {x ∈ S|hk (x) = i}, bi = |wi | und mi = 2 · bi · (bi − 1) + 1. Wähle ki mit hki (x) = (ki x mod N ) mod m. hki ist injektiv für wi P 3. Sei si = j<i mj . Speichere x in T (si +j) wobei i = (kx mod N ) mod m und j = (ki x mod N ) mod mi . Platz: m = Pn−1 i=0 mi = Pn−1 i=0 2 · bi (bi − 1) + 1 = n + 2 · Bk ≤ 9n = O(n) Laufzeit: 1. O(n) P 2. & 3. O ( |wi |) = O(n) Satz: Mit obigen Voraussetzungen kann für S eine perfekte Hashfunktion mit O(1) Zugriffszeit und Tafel der Größe O(n) in Zeit O(nN ) deterministisch und in O(n) randomisiert gefunden werden. Es gibt auch die dynamische Version, bei der S nach nud nach aufgebaut wird. Dies geht in Zeit O(n) bei einer Tafel der Größe O(n). n Beim Hashing mit Verkettung wird eine Listenlänge von 1 + m = 1+β erwartet, aber die erwartete Länge der längsten Liste ist O log n log log n . 88 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 9 MATCHING IM DATING-BUSINESS 1 Beweis: Sei S zufällig aus U gewählt, prob(h(x) = i) = m für x ∈ S, i ∈ [0, . . . , m − 1], L die Länge der längsten Liste und l(i) die Länge der Liste i. j 1 n prob(l(i) ≥ j) ≤ m m Es gilt: m−1 X prob(max l(i) ≥ j) ≤ i prob(l(i) ≥ j) i=0 1 m j n! = m· (n − j)! · j! n j−1 1 ≤ n· · m j! ≤ m· E(L) = X n j 1 m j prob(max l(i) ≥ j) j≥1 ≤ X j≥1 n Sei j0 = min j|n n j−1 m · 1 j! n j−1 1 · min 1, n · m j! o ≤ 1 ≤ min {j|n ≤ j!}, da j Es gilt j! ≥ 2j 2 , also gilt: j0 = O logloglogn n ⇒ E(L) ≤ j0 X n m ≤ 1. 1+ j=1 = O X 1 j−j0 j>j0 j0 log n log log n Beachte: Im Mittel sind Längen O(1 + β). Es gibt Listen mit Länge O log n log log n . Z UR V ORLESUNG VOM 17.07.2007 GIBT ES EINE P RÄSENTATION, DIE AUF DER V ORLESUNGSHOMEPA GE ZU FINDEN IST. I M F OLGENDEN DAHER NUR DER ZUGEHÖRIGE T AFELANSCHRIEB. 9 Matching im Dating-Business Formalisierung Eingabe: Rangordnung von einer Person zu allen anderen (weiblich/männlich) Formal: Zwei Mengen A = {1, . . . , n} und B = {1, . . . , n} Rangordnung: ∀ a ∈ A ∃ Ra : B → {1, 2, . . . , n}, Ra ist bijektiv ∀ b ∈ B ∃ Rb : A → {1, 2, . . . , n}, Rb ist bijektiv Ausgabe: Gesucht sind n Dupel. Bijektive Zuordnung M : A → B suchen (Matching), so dass M stabil ist. 89 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 9 MATCHING IM DATING-BUSINESS Was ist Stabilität? ∀ a ∈ A und (a, M (a)) | {z } =b • ∀ a 6= a ∈ A : Ra0 (b) > Ra0 (M (a0 )) und Rb (a) > Rb (a0 ) | {z } =b0 • ∀ b0 6= b ∈ B : Rb0 (a) > Rb0 (a0 ) und Ra (b) > Ra (b0 ) Beweis zu Satz 9.1: Sei M das vom Algorithmus berechnete Matching. Widerspruch: Angenommen ∃ (m, w) ∈ M , (m0 , w0 ) ∈ M , so dass • Rw (m0 ) > Rw (m) und Rm0 (w) > Rm0 (w0 ) oder • Rw0 (m) > Rw0 (m0 ) und Rm (w0 ) > Rm (w) Beweis zu stabilen Matchings: S 6= S ? , so dass m nicht mit der besten für ihn erreichbaren Frau w an einem Tisch sitzt. ⇒ w präferiert m0 Wenn m der erste Mann im Algorithmus ist, dem das passiert, ist m0 bisher nicht abgelehnt worden. ⇒ m0 findet w attraktiver als alle anderen w0 6= w Da w für m erreichbar ist, muss es S 0 geben mit (m, w) ∈ S 0 . Sei w0 die Frau, mit der m0 zusammensitzt. ⇒ Widerspruch! 90 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) INHALTSVERZEICHNIS Inhaltsverzeichnis 1 Worum geht es? 2 2 Was ist ein Algorithmus? 2 3 Analyse von Algorithmen 2 3.1 Allgemeine Komplexität . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 3.2 Asymptotische Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3.3 O-Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3.4 O-Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3.5 Ω-Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3.6 ω-Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 3.7 Θ-Notation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 4 Suchen 4 4.1 Lineare Suche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 4.2 Binäre Suche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6 4.3 Interpolationssuche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 5 Sortieren 13 5.1 Sortieren durch Einfügen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 5.2 QuickSort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 5.3 MergeSort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 5.4 HeapSort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 5.5 BucketSort . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 5.6 Auswahlproblem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 6 Balancierte Bäume 21 6.1 Bäume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 6.2 AVL-Bäume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 6.3 B-Bäume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 6.4 Kantenmarkierte Suchbäume (“Tries”) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 6.5 PATRICIA-Bäume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 6.6 Randomisierte Suchbäume: Skiplists (W. Pugh) . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 7 Graphenalgorithmen 31 7.1 Darstellung von Graphen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 7.2 Topologisches Sortieren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 7.3 Durchmusterungsalgorithmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34 7.3.1 Tiefensuche . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 7.3.2 Starke Zusammenhangskomponenten (SZKs) . . . . . . . . . . . . . . . . . . . . . . . . 38 91 www.bioinfoblog.de Algorithmen, SS 2007 (Nina Zweig, Prof. Kaufmann) 8 Hashing INHALTSVERZEICHNIS 85 8.1 Hashing mit Verkettung (Chaining) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 8.2 Perfektes Hashing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 9 Matching im Dating-Business 89 92 www.bioinfoblog.de