1 Einleitung DAP 2 1.1 Ziele und Überblick Datenstrukturen, Algorithmen und Programmierung 2 Buch “Fundamentals of Software Engineering” (Ghezzi, Jazayeri, Mandrioli): “A software engineer must of course be a good programmer, be well-versed in data structures and algorithms ...” Ingo Wegener Also machen wir DAP 2 mit dem Schwerpunkt . – Seite 1/726 . – Seite 2/726 Datenstrukturen und effiziente Algorithmen – Nachweis von Effizienz durch Experimente? Entwurf, Korrektheit, Implementierung und Analyse Experimente sind ein wichtiges Hilfsmittel, aber – vielleicht für manche Anwendungen untypische Eingaben, Effizienz ist überall ein notwendiges Kriterium für gute Informatikprodukte. – Aussagen beschränkt auf die gewählten Eingaben und Eingabelängen (die typischerweise mit der Zeit wachsen). Besser ein Qualitätsprodukt mit Gütenachweis. Was heißt das? . – Seite 3/726 . – Seite 4/726 – Korrektheitsbeweis für den Algorithmus, Wie entwirft man einen effizienten Algorithmus? – Korrektheitsbeweis für die Implementierung (→ DAP 1, Semantik), Wie malt man ein Meisterwerk? – Analyse der vom Algorithmus benötigten Ressourcen, hier insbesondere Rechenzeit, prinzipiell auch Speicherplatz, ... Hat dies auch praktische Bedeutung? – Ermöglicht Vergleich von Algorithmen, auch für künftig zu bewältigende Eingabelängen. Geschick, Intuition, Glück, Erfahrung, Spezialkenntnisse und Handwerk. Hier nur Probleme, für die keine Spezialkenntnisse aus anderen Gebieten nötig sind. Wir können nur das nötige Handwerkszeug lehren/lernen und Erfahrungen sammeln. – Rechenzeitanalyse deckt Schwachstellen auf und gibt Hinweise zur Verbesserung des Algorithmus. . – Seite 5/726 . – Seite 6/726 Was sind nun Datenstrukturen? Beispiel Bestimmte Formen der Speicherung von Daten eines bestimmten Typs, die eine gegebene Menge von Operationen auf den Daten effizient unterstützen. a1 , . . . , an Folge von Objekten. Füge b „zwischen“ ai und ai+1 ein. Algorithmen setzen Datenstrukturen ein und Datenstrukturen benötigen Algorithmen für die Realisierung der Operationen. Rechenzeit hängt stark von der verwendeten Datenstruktur ab: – Direkter Zugriff auf ai oder muss ai erst gesucht werden? – Gibt es Platz „hinter“ ai oder muss der erst geschaffen werden? . – Seite 7/726 . – Seite 8/726 Auswahl der von uns untersuchten Probleme Überblick über die Vorlesung – Sie sind „interessant“ und „wichtig“, sind oft der Kern vieler Anwendungsprobleme. Noch in diesem Kapitel: – Sie sind geeignet, um Konzepte, allgemeine Prinzipien und Methoden zu erlernen. – zwei Probleme, an denen unser Vorgehen exemplarisch demonstriert wird, – ein Rechnermodell als theoretisches Referenzmodell, Vorgehen unabhängig von speziellen Rechnern und Programmiersprachen. – Effizienzmaße, worst case und average case Rechenzeit, randomisierte Algorithmen, – Größenordnungen und die O-Notation. – Implementierungen in den Übungen. – Umgang mit Programmbibliotheken wie LEDA. . – Seite 9/726 . – Seite 10/726 Kapitel 2: Grundlegende Datenstrukturen Zunächst gehen wir anders vor. Problemorientiertes Vorgehen: Wir betrachten die schon bekannten Datenstrukturen (Arrays, Stacks, Queues, Listen, Datenstrukturen für Mengen, Bäume und Graphen) und fragen uns, wie effizient sie die verschiedenen Operationen unterstützen. Gegeben: Typ der Daten und eine Liste von Operationen auf den Daten. Gesucht: Datenstruktur zur effizienten Speicherung der Daten und zur effizienten Ausführung der Operationen. . – Seite 11/726 . – Seite 12/726 Kapitel 3: Dynamische Datenstrukturen Danach entwickeln wir prototypisch für drei Probleme statische Datenstrukturen. Daten aus einer ungeordneten/geordneten Menge. Statisch heißt, dass die Datenmenge während der Anwendung konstant ist. Operationen: Suchen, Einfügen, Entfernen, auch: Konkatenieren, Trennen. Die Entwicklung von Datenstrukturen wird von konkreten Problemen initiiert, ist aber dann unabhängig von einer konkreten Fragestellung. Listen und Arrays unterstützen nicht alle drei zentralen Operationen! Lösungen: → Entwicklung der Datenstrukturen in Kap. 2. → Anwendung der Datenstrukturen in Kap. 5. – Hashing (auch für ungeordnete Mengen), – balancierte Suchbäume (nur für geordnete Mengen), – (randomisierte) Skiplisten (nur für geordnete Mengen). . – Seite 13/726 . – Seite 14/726 Zusätzlich: Variationen des Themas: Kapitel 4: Sortieren Früher: Sortieren – eine der zentralen Anwendungen von Algorithmen. – Sortieren auf Multiprozessorsystemen, – randomisierte Varianten der Algorithmen, Heute: Gute Sortieralgorithmen im Rechnersystem vorhanden. – Auswahlproblem, – effizienteres Sortieren spezieller Daten. Über kein algorithmisches Problem sind mehr Arbeiten geschrieben worden als über Sortieren. Diskussion der Vor- und Nachteile bekannter Sortierverfahren als exemplarisches Beispiel für Entwurf, Implementierung und Analyse von Algorithmen. . – Seite 15/726 . – Seite 16/726 Kapitel 5: Entwurfsmethoden für Algorithmen Wann werden die Ergebnisse beliebig schlecht? – TSP (Traveling Salesperson Problem), KP (Knapsack Problem), Geldwechselprobleme. Allgemeine Strategien zur Bearbeitung von Optimierungsproblemen Wann werden die Ergebnisse immer gut, aber nicht immer optimal? Wähle gierig das beste Einzelstück, ohne die Folgen für die Zukunft zu bedenken. Greedy Algorithmen: – BP (Bin Packing). Wann erhalten wir optimale Resultate? – Minimale Spannbäume, Verwendung von DS für Partitionen. . – Seite 17/726 Effizientes iteratives Vorgehen, wenn rekursives Vorgehen ineffizient wird, weil viele Teilprobleme sehr oft gelöst werden. Dynamische Programmierung: – Optimale statische Suchbäume, KP, APSP (all pairs shortest paths), optimale Alignments. Hybridisierung von greedy Strategien und dynamischer Programmierung: Hybridisierung: Verbindung der Vorteile zweier Strategien. . – Seite 18/726 Heuristischer Ansatz mit garantiert optimaler Lösung, aber nur erhofft guter Rechenzeit. Maximiere f : S → R unter bestimmten Nebenbedingungen, S endlich, aber groß, z.B. S = {0, 1}200 . Vollständiges Durchsuchen von S unmöglich. Versuche den Suchraum so zu partitionieren, dass Informationen über einen Teil beweisen, dass andere Teile nicht durchsucht werden müssen. Branch-and-Bound Algorithmen: – SSSP (single source shortest paths). – KP. . – Seite 19/726 . – Seite 20/726 Eine zentrale Technik aus der algorithmischen Geometrie (Bildverarbeitung), um d-dimensionale Probleme „fast“ auf (d-1)-dimensionale Probleme zu reduzieren. Effiziente Zerlegung des Problems in möglichst wenige, möglichst kleine Probleme vom selben Typ, so dass Lösung des Gesamtproblems effizient aus der Lösung der Teilprobleme zusammengesetzt werden kann. Sweepline-Technik: Divide-and-Conquer Algorithmen: – Rechtecküberdeckungsproblem, Verwendung von DS für Intervalle. – Matrixmultiplikation schneller als in Linearer Algebra, – Polynommultiplikation, FFT (Fast Fourier Transform), – NN (Nearest Neighbors) von n Punkten in der Ebene. . – Seite 21/726 Eine zentrale Technik z.B. für schnelle Schachprogramme, in vielen Gebieten der KI (Künstliche Intelligenz) eingesetzt. Analyse von Spielbäumen: . – Seite 22/726 Randomisierte Suchheuristiken: Strategien, wenn alles andere versagt. – Lokale Suche, – α-β -Pruning – randomisierte lokale Suche, – Tabu Suche, – Metropolis Algorithmus, – Simulated Annealing, – evolutionäre und genetische Algorithmen. . – Seite 23/726 . – Seite 24/726 Lernziele – Erfahrung in der Anwendung von DS und Entwurfsmethoden, – Kenntnis elementarer DS, ihrer Eigenschaften, Vor- und Nachteile, – Erfahrung in der Umsetzung von DS und Algorithmen in lauffähige Programme, – Kenntnis wichtiger Entwurfsmethoden für effiziente Algorithmen, – Kenntnis von Methoden, um die Effizienz von DS und Algorithmen zu messen und von Anwendungen dieser Methoden. – Kenntnis effizienter Algorithmen für grundlegende Probleme, . – Seite 25/726 Realisierung . – Seite 26/726 – Selbstständiges Aufschreiben der Lösungen zu den Aufgaben. – Ständige Bereitschaft, die Lösungen in den Übungen vorzustellen. – Ständiger aktiver Besuch der Vorlesungen. – Notizen zu Skript und Folien in der Vorlesung. – Texte in eigenen Worten zu den Themen der Vorlesung. – Eigenständige Bearbeitung der Übungsaufgaben bzw. aktive Teilnahme in kleinen Arbeitsgruppen. . – Seite 27/726 – Fragen stellen in Vorlesung und Übungen. – Fragen beantworten in Vorlesung und Übungen. . – Seite 28/726 Tipps zur Vorbereitung der Klausur im Laufe der Vorlesung. In der Klausur kann ALLES aus der Vorlesung drankommen! 1.2 Literatur Siehe Skript. . – Seite 29/726 1.3 Das Maxsummenproblem . – Seite 30/726 Motivation: Lokale Ähnlichkeit in zwei Folgen x und y von Aminosäuren, wobei ai die Ähnlichkeit von xi und yi misst. Gegeben: Array der Länge n mit ganzen Zahlen a1 , . . . , an . Suchraum: Alle Intervalle [i, j], 1 ≤ i ≤ j ≤ n. Allgemein: Global Alignment, wobei x und y mit Leerstellen aufgefüllt werden dürfen. Bewertung: [i, j] → f (i, j) := ai + · · · + aj . Ziel: Maximierung. Finde ein Paar (i∗ , j ∗ ) mit f (i∗ , j ∗ ) ≥ f (i, j) für alle 1 ≤ i ≤ j ≤ n und den zugehörigen Wert f (i∗ , j ∗ ). . – Seite 31/726 . – Seite 32/726 Algorithmus 1.3.1 (der naive Algorithmus) Spielregeln: – Additionen und Vergleiche kosten eine Einheit, Berechne alle f (i, j) und bestimme das Maximum. – Zuweisungen, . . . sind umsonst. – Berechnung von f (i, j) = ai + · · · + aj kostet j − i Additionen. M AX := a1 (= f (1, 1)) (I := (1, 1)) for i = 1 to n do for j = i to n do berechne s := f (i, j), if s > M AX then M AX := s output M AX, I – Berechnung des Maximums von k Zahlen kostet k − 1 Vergleiche. (Der Algorithmus macht einen unsinnigen Vergleich, welchen?) Vorbemerkung: (I := (i, j)) In Vorlesung und Skript bevorzuge ich textuelle Beschreibungen von Algorithmen. . – Seite 33/726 . – Seite 34/726 Analyse des naiven Algorithmus Anzahl der Additionen: Anzahl der Paare (i, j), 1 ≤ i ≤ j ≤ n: A1 (n) = X (i,j)|1≤i≤j≤n Es gibt j Paare (i, j), 1 ≤ i ≤ j , also P j = 12 n(n + 1) Paare. (j − i) = X 1≤i≤n X i≤j≤n | (j − i) {z } 0+1+2+···+n−i= 12 (n−i)(n−i+1) 1≤j≤n = X 1 (n − i)(n − i + 1) 2 1≤i≤n Anzahl der Vergleiche (ohne den unsinnigen): V1 (n) = 12 n(n + 1) − 1 = 12 n2 + 12 n − 1. . – Seite 35/726 . – Seite 36/726 Algorithmus 1.3.2 (der normale Algorithmus) = 1 2 P 1≤i≤n n2 − 2n P P i+ 1≤i≤n i2 + 1≤i≤n P 1≤i≤n n3 −2n n(n+1) = −n3 − n2 2 1 n(n 6 = 1 3 6n − n2 n− P 1≤i≤n i Naiv: f (i, j) = ai + · · · + aj ist bekannt, trotzdem wird f (i, j + 1) = ai + · · · + aj + aj+1 mit j + 1 − i Additionen berechnet! − 12 n(n + 1) = − 12 n2 − 12 n Nun f (i, j + 1) = f (i, j) + aj+1 und f (i, i) = ai . V2 (n) = 12 n2 + 12 n − 1 + 1)(2n + 1) = 13 n3 + 12 n2 + 16 n (wie V1 (n)). Eine Addition für jedes Paar (i, j) mit i < j , also für jedes der 12 n2 + 12 n Paare bis auf n Paare. 1 6 n. T1 (n) = V1 (n) + A1 (n) = 16 n3 + 12 n2 + 13 n − 1. A2 (n) = 12 n2 − 12 n. T2 (n) = V2 (n) + A2 (n) = n2 − 1. . – Seite 37/726 Algorithmus 1.3.3 (Divide-and-Conquer) . – Seite 38/726 Nebenbetrachtung Suchraumgröße: Zur Vereinfachung n = 2k . 1≤i≤j≤n 1 2 n 2 1≤i≤j≤n 1 ≤ i ≤ j ≤ n/2 1 n 2 ( ) 2 2 1 ≤ i ≤ j ≤ n/2 I · n 2 = 18 n2 + 14 n 1 ≤ i ≤ n/2 < j ≤ n n/2 + 1 ≤ i ≤ j ≤ n II + 1 2 + 12 n 1 ≤ i ≤ n/2 < j ≤ n n 2 · n 2 = 14 n2 n/2 + 1 ≤ i ≤ j ≤ n 1 n 2 ( ) + 12 n2 2 2 = 18 n2 + 14 n III Suchraumgröße und Schwierigkeit sind nicht unbedingt korreliert. Löse Probleme I, II und III und bestimme mit zwei Vergleichen die Gesamtlösung. Probleme I und III sind vom selben Typ wie das Ausgangsproblem, aber Arraylänge ist halbiert. Das mittlere Problem ist separabel und daher einfacher. . – Seite 39/726 . – Seite 40/726 n 2 n 2 +1 (i, j) f (i, j) ai + · · · + an/2 = | = {z h(i) + an/2+1 + · · · + aj } + | {z g(j) } h und g hängen nur von einem Parameter ab, beeinflussen sich nicht und können unabhängig maximiert werden. Berechne h(n/2) = an/2 , ... h(i) = h(i + 1) + ai , ... h(1) = h(2) + a1 und bestimme hmax mit n2 − 1 Additionen und n2 − 1 Vergleichen. Berechne analog gmax und hmax + gmax als Lösungswert für Problem II. Gesamtkosten: 2n − 3 . – Seite 41/726 . – Seite 42/726 Die Analyse von rekursiven Algorithmen führt zu Rekursionsgleichungen. – Ein paar Mal weiter einsetzen. Betrachtung der Gesamtzeit T3 (2k ). – Lösung raten. Anfangsbedingung T3 (1) = 0. – Lösung mit Induktionsbeweis verifizieren. Rekursionsgleichung: T3 (2k ) = T3 (2k−1 ) + 2 · 2k − 3 + T3 (2k−1 ) + 2 I II III Gesamtlösung = 2 · T3 (2k−1 ) + 2 · 2k − 1 UND NUN? . – Seite 43/726 . – Seite 44/726 T3 (2k ) = 2 · T3 (2k−1 ) + 2 · 2k − 1 Jetzt darf jede/jeder raten. P k+1 ! T3 (2k ) = 2l · T3 (2k−l ) + (2 − 2i−1 ) (T3 (2k−1 ) = 2 · T3 (2k−2 ) + 2 · 2k−1 − 1) = 2 · (2 · T3 (2k−2 ) + 2 · 2k−1 − 1) + 2 · 2k − 1 2 (T3 (2 k−2 = 2 · T3 (2 ) = 2 · T3 (2 k−2 k−3 2 ) + (2 k+1 )+2·2 = 2 · (2 · T3 (2 k−3 1 − 2 ) + (2 k−2 k+1 − 1) 1≤i≤l 0 −2 ) Verifikation mit Induktionsbeweis. ) + 2 · 2k−2 − 1) +(2k+1 − 21 ) + (2k+1 − 20 ) = 23 · T3 (2k−3 ) + (2k+1 − 22 ) +(2k+1 − 21 ) + (2k+1 − 20 ) . – Seite 45/726 . – Seite 46/726 Und was haben wir davon? Algorithmus 1.3.4 (Dynamische Programmierung) Wir kennen T3 (20 ) = 0, also setze l := k . Hier nicht als allgemeine Methode (→ Kapitel 5) Pn (Problem der Größe n) T3 (2k ) = 2k · T3 (20 ) + X 1≤i≤k (2k+1 − 2i−1 ) 1≤i≤j≤n = 0 + k · 2k+1 − (20 + 21 + · · · + 2k−1 ) n = k · 2k+1 − (2k − 1) 1 ≤ i ≤ j ≤ n − 1 Pn−1 Setze n = 2k , also k = log n: T3 (n) = 2n log n − n + 1. n 1≤i≤j=n Sn (Spezialproblem der Größe n) Löse iterativ P1 = S1 , S2 , P2 , S3 , P3 , . . . , Sn , Pn . Hier nur Werte der Lösungen betrachtet. . – Seite 47/726 . – Seite 48/726 Lösung von Sk Ak Bk Wert der Lösung von Pk Wert der Lösung von Sk A1 = B 1 = a1 k 1≤i≤j=k (keine Wahlmöglichkeit) k k 1 ≤ i ≤ k − 1, j = k i = k, j = k Ak−1 und Bk−1 sind bekannt. Annahme Lösung ak k−1 + k 1 ≤ i ≤ k − 1, j = k − 1 Lösung Bk−1 + ak → Bk = max{Bk−1 + ak , ak }, Kosten: 2. . – Seite 49/726 . – Seite 50/726 Lösung von Pk naiv 1 3 n 6 n Pk Sk Pk−1 −→ Ak = max{Ak−1 , Bk }, Kosten: 1. normal + 1 n 3 −1 n2 −1 Divide-and-Conquer Dyn. Prog. (2 log n − 1)n + 1 3n − 3 22 = 4 24 = 16 815 255 113 45 26 = 64 45759 4095 705 189 28 = 256 2829055 65535 3841 765 210 = 1024 179481599 1048575 19457 3069 215 = 32768 > 5 · 1012 ≈ 109 950273 98301 ≈ 106 1017 ≈ 1012 40894463 1048573 220 Kosten für (Sk , Pk ), 2 ≤ k ≤ n, je 3. + 1 2 n 2 19 > 1, 9 · 15 13 9 T4 (n) = 3n − 3. . – Seite 51/726 . – Seite 52/726 1.4 Das MAXMIN-Problem Andere Problemformulierung: Ziel: Algorithmische Lösung eines Problems und der Nachweis, dass der Algorithmus optimal ist. Gegeben: Array der Länge n mit n verschiedenen Zahlen a1 , . . . , a n . Aufgabe: Berechne i, j ∈ {1, . . . , n}, so dass ai < ak < aj für alle k ∈ {1, . . . , n} − {i, j}, also kleinste und größte Zahl. Spielregeln: n Tennisspieler unterschiedlicher, aber unbekannter Spielstärke. Unter der Annahme, dass stets der Bessere ein Spiel gewinnt, bestimme kürzesten Turnierplan, der den Champion und den Absteiger bestimmt. Algorithmenklasse 1.4.1 1. Bestimme den Champion. 2. Bestimme unter allen Spielern, die noch kein Spiel gewonnen haben, den Absteiger. n − 1 Spiele k − 1 Spiele, wenn k Spieler daran teilnehmen. – Vergleiche kosten eine Einheit, – Zuweisungen, ... sind umsonst. . – Seite 53/726 Idee 1: . – Seite 54/726 a1 > a 2 > · · · > a n : a2 , . . . , an in der Abstiegsrunde → n − 1 + n − 2 = 2n − 3 Spiele a1 < a 2 < · · · < a n : a1 in der Abstiegsrunde → n − 1 + 0 = n − 1 Spiele Championrunde: Spieler 1 spielt so lange gegen Spieler, bis er gegen Spieler i verliert. Alle Verlierer scheiden aus. Spieler i setzt dieses System unter den verbliebenen Spielern fort. (So haben wir das Maximum von n Zahlen bestimmt.) Wir wollen die Anzahl der Spiele im schlechtesten Fall minimieren. . – Seite 55/726 . – Seite 56/726 n = 2k + 1 ungerade Idee 2 (Algorithmus 1.4.2): n = 2k gerade k Ausscheidungsspiele 1, . . . , 2k 1 ↔ 2, 3 ↔ 4, . . . , 2k − 1 ↔ 2k k Sieger in der k Verlierer in der Championrunde Abstiegsrunde k − 1 Spiele k − 1 Spiele k Spiele Championrunde + Spieler n 3k − 2 = 32 n − 2 Spiele Abstiegsrunde + Spieler n k Spiele k Spiele 3k = 3 n−1 = 32 n − 2 In beiden Fällen n + dn/2e − 2 Spiele. . – Seite 57/726 Satz 1.4.3: Das MAXMIN-Problem kann mit n + dn/2e − 2 Vergleichen, aber nicht mit weniger Vergleichen gelöst werden. 3 2 Spiele . – Seite 58/726 Wie können Spiele Informationen erzeugen? Also ist Algorithmus 1.4.2 optimal! Obere Schranke bereits erledigt. Nun müssen wir alle Turnierpläne mit n + dn/2e − 3 Spielen ausschließen. Ziel: Champion und Absteiger. Äqivalent n − 1 Nichtchampions und n − 1 Nichtabsteiger. 2n − 2 Informationseinheiten . – Seite 59/726 Einteilung der Spieler in vier Kategorien ? - Spieler haben noch nicht gespielt + - Spieler haben gewonnen, aber nicht verloren Nichtabsteiger, vielleicht Champion − - Spieler haben verloren, aber nicht gewonnen ± - Spieler haben schon gewonnen und verloren Nichtabsteiger und Nichtchampion 0 Informationseinheiten 1 Informationseinheit 1 Informationseinheit 2 Informationseinheiten . – Seite 60/726 Möglichkeiten der Informationserzeugung ? + − ± ? 2 1/2 1/2 1 + 1/2 1 0/2 0/1 − 1/2 0/2 1 0/1 Jeder Spieler ist nur in einem Spiel ?-Spieler. ⇒ k ≤ bn/2c Spiele mit 2 Informationseinheiten ± 1 0/1 0/1 0 ⇒ 2k ≤ 2bn/2c Informationseinheiten aus ?/?-Spielen ⇒ 2n − 2 − 2k Informationseinheiten aus Spielen mit maximal einer Informationseinheit ⇒ Anzahl der Spiele ≥ k + 2n − 2 − 2k = 2n − k − 2, wobei k ≤ bn/2c. Worst Case Analyse Für k = bn/2c untere Schranke 2n − bn/2c − 2 = n + dn/2e − 2. ↑ n = bn/2c + dn/2e Der Teufel legt Spielausgänge fest. Der Teufel vergibt 2 Informationseinheiten nur in ?/? - Spielen. . – Seite 61/726 . – Seite 62/726 Große Ausnahme: Beweis der Optimalität eines Algorithmus. 1.5 Registermaschinen (random access machines, RAMs) Nützlich: Veranschaulichung durch Turniere, Champions, Absteiger. Kategorisierung des Status der Spieler im Verlauf des Turniers. Finden des richtigen Parameters „Informationseinheit“. Negatives Denken: Nichtabsteiger und Nichtchampions. Müssen wir die Spielregeln (was kostet was?) immer neu festlegen? . – Seite 63/726 Das wäre mühselig und ungerecht! Hängt die Rechenzeit nicht wesentlich von Hardware und Programmiersprache ab? Dann wären objektive Maße unmöglich! Hardware kann Rechenzeit stark senken → Maß: Rechenschritte. . – Seite 64/726 Die Zahl der Rechenschritte ist ein stabiles Maß. Programm Ein konkreter Rechner ist bezüglich eines theoretischen Referenzmodells maximal um einen konstanten Faktor schneller. Gilt das auch für zukünftige Rechner? (→ GTI) z Programme sind Listen von Befehlen. Speicher ist linear angeordnet. Alle Daten sind natürliche Zahlen inklusive Null. Zeile des Programms c(0) c(1) c(2) c(3) c(4) ... Speicher Recheneinheit . – Seite 65/726 Grundbefehle Wirkung LOAD i STORE i ADD i SUB i MULT i DIV i GOTO j IF c(0) ? ` GOTO j c(0) := c(i), z := z + 1. c(i) := c(0), z := z + 1. c(0) := c(0) + c(i), z := z + 1. c(0) := max{c(0) − c(i), 0}, z := z + 1. c(0) := c(0) · c(i), z := z + 1. c(0) := bc(0)/c(i)c, z := z + 1. z := j . z := j , falls c(0) ? ` wahr, z := z + 1 sonst ? ∈ {=, <, ≤, >, ≥}. z := z . END . – Seite 67/726 . – Seite 66/726 Befehlsvarianten C LOAD, C ADD, C SUB, C MULT, C DIV mit Konstante i statt c(i). INDLOAD, INDSTORE, INDADD, INDSUB, INDMULT, INDDIV mit c(c(i)) statt c(i) (indirekte Adressierung). Registermaschinen widersprechen allen Ideen aus DAP 1, daher kein konkretes Programm für Registermaschinen. Aber alles geht. Damit kann Rechenzeit sauber definiert werden. . – Seite 68/726 1.6 Effizienzmaße TP (x) := Einheitliches oder uniformes Kostenmaß: Kosten 1 für jeden Befehl außer END. Logarithmisches Kostenmaß: Kosten eines Befehls außer END: Anzahl der beteiligten Bits. Das logarithmische Kostenmaß ist realistischer. Bei Algorithmen ohne „große Zahlen“ uniformes Kostenmaß einfacher handhabbar und nahe genug an der Wahrheit. Anzahl der Rechenschritte von Programm P bei Eingabe x. T ↔time log TP (x) analog für logarithmisches Kostenmaß. SP (x) := Anzahl der von P auf x benutzten S ↔ space Register. log SP (x) für jedes benutzte Register die Maximalzahl der gespeicherten Bits berücksichtigt. Die gemeinsame Minimierung von Zeit und Speicherplatz ist wünschenswert, aber manchmal unmöglich. Time-Space Trade-off . – Seite 69/726 Bestimmung von TP (x) für alle x meistens unmöglich → Vergröberung nötig. . – Seite 70/726 Worst Case Rechenzeit Wir fassen die Eingaben zu sinnvollen Gruppen zusammen: → Eingaben x gleicher Eingabelänge |x|. Definition der Eingabelänge kann vereinbart werden: Tp (n) := Tpwc (n) := sup{Tp (x) | |x| = n, x Eingabe für P }. Best case Rechenzeit Tpbc (n) := inf{Tp (x) | |x| = n, x Eingabe für P }. Average Case Rechenzeit zur Wahrscheinlichkeitsverteilung qn auf den Eingaben der Länge n – Multiplikation: Anzahl der Bits der Faktoren. – Graphen: Anzahl der Knoten, aber auch zwei Parameter denkbar: n Knoten und m Kanten. ac Tp,q (n) n := Eqn (Tp (x)) Erwartungswert, gewichteter Mittelwert P = Tp (x) · qn (x). x:|x|=n, x Eingabe für P . – Seite 71/726 . – Seite 72/726 Diskussion der Vor- und Nachteile In jedem Fall exakte Berechnung meist zu schwierig −→ untere und obere Schranken. Best case: nur für unerschrockene Optimisten. Auch die schwierigsten Probleme haben einfache Fälle, die von Algorithmen schnell gelöst werden können. −→ Kap. 1.7 Average case: realistisch, wenn eine realistische Verteilung qn bekannt ist (was selten der Fall ist). Oft schwer zu berechnen. Zuvor ein Beispiel, in dem average case und worst case Rechenzeit exakt berechenbar sind, sich stark unterscheiden und der average case realistischer ist. Worst case: oft realistisch. Oft sind die meisten Eingaben ungefähr gleich schwierig und sie gehören zu den schwierigsten Eingaben. Manchmal zu pessimistisch. . – Seite 73/726 . – Seite 74/726 i := i + 1 Eingaben der Bitlänge n, also i ∈ {0, 1, . . . , 2n − 1} Kostenmaß: Anzahl der Bitoperationen 01111 | {z } k−1 Average case Rechenzeit nach Definition P Kosten k für 2n−k Zahlen 0≤i≤2n −1 10000 11 = 2−n ( 111 000 X 1≤k≤n Kosten n + 1 für eine Zahl 100 (Rechenzeit bei Eingabe i) · 2−n | 2n−k Eingaben mit Rechenzeit k 2n−k · k {z } + n + 1) 1 Eingabe mit Rechenzeit n + 1 Wie kriegen wir das raus? Dies ist auch die Taktzahl des von Neumann Addierwerkes. Worst case: n + 1. Average case bei Gleichverteilung auf {0, 1, . . . , 2n − 1}? . – Seite 75/726 . – Seite 76/726 Alternative Rechnung mit Hilfe von Potenzialfunktionen P 1≤k≤n 2n−k · k = 2n−1 + 2 · 2n−2 + 3 · 2n−3 + · · · + n · 2n−n = 2n−1 + 2n−2 + + 2n−2 + + 2n−3 + · · · + 2n−n 2n−3 + · · · + 20 2n−3 + · · · + 20 +··· + 20 Die Rechenzeit wird mit einer anderen Größe verknüpft, die einfacher zu behandeln ist. = 2n − 1 +2n−1 − 1 +2n−2 − 1 +··· +21 − 1 = 2n+1 − 2 − n Average case Rechenzeit P n−k 2−n · ( 2 · k + n + 1) = 2−n · (2n+1 − 2 − n + n + 1) 1≤k≤n = 2 − 2−n . Hier: Anzahl der Einsen in der Binärdarstellung Änderung im Suffix der Zahlen 0 01 011 k−1 z }| { 01...1 Kosten Zuwachs an Einsen Kosten + Zuwachs → → → 1 10 100 1 2 3 +1 0 −1 2 2 2 → 10 . . . 0 k −(k − 2) 2 ci := Kosten für i := i + 1 di := Zuwachs an Einsen bei i := i + 1 −→ ci + di = 2 . – Seite 77/726 Unser Interesse: Einfach: P 2−n ci . 0≤i≤2n −1 P d i = 1. . – Seite 78/726 Eine weitere Alternative aus einem anderen Blickwinkel Wie oft wird das Bit an Position i verändert? 0≤i≤2n −1 (Wir starten mit i = 0 mit 0 Einsen und enden mit i = 2n mit 1 Eins.) P Also 2−n ci 0≤i≤2n −1 P = 2−n (2 − di ) 0≤i≤2n −1 P = 2−n · 2 · 2n − 2−n di = 2 − 2−n . 0≤i≤2n −1 Cleverness kann Rechenzeit ersparen. Manche Analysen ohne Potenzialfunktionen gar nicht machbar. Position 0 Position 1 Position i jedes Mal jedes zweite Mal jedes 2i -te Mal zusammen 2n Operationen 2n−1 Operationen 2n−i Operationen 2n+1 − 1 Durchschnittliche Anzahl an Operationen: (2n+1 − 1)/2n = 2 − 2−n . Wechsel des Blickwinkels kann sehr nützlich sein. . – Seite 79/726 . – Seite 80/726 f = O(g) 1.7 Größenordnungen und die O-Notation gesprochen: groß Oh erstmals Bachmann (1892) Rechenzeiten selten exakt bestimmbar. Rechenzeiten sind zwar ganzzahlig, aber Abschätzungen können zu nicht ganzen Zahlen führen, z.B. P i = 12 (n − 1)n ≤ n2 /2. 1≤i≤n−1 Also: Wachstumsordnung oder Größenordnung von f : N → R+ . f ≤ g :⇔ f (n) ≤ g(n) für alle n ∈ N ist zu strikt. Nun „asymptotisch ≤“. Interpretation: f wächst asymptotisch nicht schneller als g . Definition: f (n)/g(n) ist durch eine Konstante c nach oben beschränkt. Achtung: Auch wenn f = O(g) ist, ist O(g) = f sinnlos (nicht definiert). Denke bei O an “≤”, also nur von links nach rechts lesbar. Denkbar: O(g) ist die Menge aller f mit f = O(g), dann suggestiver f ∈ O(g). . – Seite 81/726 O(f ) = O(g) :⇔ h = O(f ) ⇒ h = O(g). Rechenregeln – c ≥ 0 ⇒ c · f = O(f ). n2 + dn1/2 e ≤ n2 + n = O(n2 ) = O(n3 ) trivial n2 +n n2 ≤1+ 1 n ≤2 h(n) n2 . – Seite 82/726 – c · O(f ) = O(f ). ≤c⇒ h(n) n3 – O(f1 ) + · · · + O(fk ) = O(f1 + · · · + fk ) = O(max{f1 , . . . fk }) für k konstant, da c1 f1 (n) + · · · + ck fk (n) ≤ (c1 + · · · + ck ) · (f1 (n) + · · · + fk (n)) ≤ k · (c1 + · · · + ck ) · max{f1 (n), . . . , fk (n)}. ≤c ⇒ n2 + n = O(n3 ). Schreibweise mit ∈ und ⊆ würde alles mischen: n2 + dn1/2 e ≤ n2 + n ∈ O(n2 ) ⊆ O(n3 ). – O(f ) · O(g) = O(f · g), da (c1 f (n)) · (c2 g(n)) = (c1 · c2 ) · (f · g(n)). . – Seite 83/726 . – Seite 84/726 Vergleichbarkeit von Funktionen groß Omega, asymptotisch ≥, asymptotisch mindestens so schnell :⇔ g = O(f ). f = Ω(g) f (n) = n2 , g(n) = n + 10, mit ≤ nicht vergleichbar, da f (1) < g(1), aber f (100) > g(100), f = Θ(g) groß Theta, asymptotisch =, asymptotisch gleich schnell :⇔ f = O(g) und f = Ω(g). aber n + 10 = O(n2 ), sogar n + 10 = o(n2 ). f = o(g) klein Oh, asymptotisch <, asymptotisch langsamer :⇔ f (n)/g(n) ist Nullfolge. f = ω(g) klein Omega, asymptotisch >, asymptotisch schneller :⇔ g = o(f ). . – Seite 85/726 Nicht alle monotonen Funktionen sind asymptotisch vergleichbar ( n! n gerade f (n) := (n − 1)! n ungerade ( (n − 1)! n gerade g(n) := n! n ungerade . – Seite 86/726 Ordnung der wichtigsten Funktionen Basisfunktionen: 1, log log n, log n, n, 2n , 22 n 1 = o(log log n), die anderen Funktionen unterscheiden sich exponentiell, d. h. 2log log n = log n, 2log n = n und 2(2 ∀ Konstanten k > 0, ε > 0: f und g monoton wachsend, aber f (n)/g(n) = n für n gerade und g(n)/f (n) = n für n ungerade. → Weder f = O(g) noch g = O(f ). Aber: Rechenzeiten von „vernünftigen“ Algorithmen sind (log log n)k logk n nk 2n asymptotisch vergleichbar. . – Seite 87/726 k = o(logε n) = o(nε ) ε = o(2n ) n ) n = 22 . logk n ist Kurzform für (log n)k nε = o(22 ) . – Seite 88/726 Exemplarisch: Beweis von logk n = o(nε ). Zeige logk n nε Anwendung des Satzes von Bernoulli und de l’Hospital: [f (x) → ∞, g(x) → ∞ für x → ∞ 0 (x) (x) = lim fg0 (x) , falls existent.] ⇒ lim fg(x) → 0 für n → ∞. Analysis: an Nullfolge ⇔ aαn für α > 0 Nullfolge. x→∞ Hier α := 1/k , δ := ε/k . Zeige log n nδ d dx log x d δ dx x → 0 für n → ∞. Kanonische Fortsetzung auf R. Zeige log x xδ x→∞ = 1 x ln 2 δxδ−1 = 1 −δ x → 0 für x → ∞. δ ln 2 Für uns wichtige Anwendung: → 0 für x → ∞. n log n = o(n2 ), da log n = o(n). . – Seite 89/726 Wichtige Funktionen für Rechenzeiten geordnet nach Größenordnung (0 < ε < 1): . – Seite 90/726 Die Größe der Summe von konstant vielen vergleichbaren Größenordnungen ist die größte dieser Größenordnungen, z.B. 1 10n2 log2 n + 2n3 / log n + 5n = Θ(n3 / log n). log log n Da c · nα = Θ(nα ) und „α < β ⇒ nα = o(nβ )“, gilt X ci · nαi = Θ(nα ) log n, log2 n, log3 n, . . . nε , n, n log n, n log n log log n, n log 2 n, n1+ε , n2 , n3 , . . . ε 2n , 2εn , 2n , . . . 1≤i≤k für ci , αi > 0, k konstant und α = max{α1 , . . . , αk }. n 22 . Kurz: Bei Polynomen entscheidet der höchste Exponent über die Größenordnung. . – Seite 91/726 . – Seite 92/726 f : N → R+ heißt Aufgepasst: n2 ist also kubisch wachsend (genauer asymptotisch nicht schneller als kubisch wachsend). logarithmisch wachsend, wenn f = O(log n), polylogarithmisch wachsend, wenn f = O(logk n) für ein k ∈ N, linear wachsend, wenn f = O(n), quadratisch wachsend, wenn f = O(n2 ), kubisch wachsend, wenn f = O(n3 ), f : N → R+ heißt ε exponentiell wachsend, wenn f = Ω 2n für ein ε > 0, echt exponentiell wachsend, wenn f = Ω (2εn ) für ε > 0. Dies sind untere Schranken. Was ist mit nlog n ? Weder polynomiell wachsend noch exponentiell wachsend, sondern quasipolynomiell. quasilinear wachsend, wenn f = O(n logk n) für ein k ∈ N, polynomiell wachsend, wenn f = O(nk ) für ein k ∈ N. Dies sind obere Schranken. . – Seite 93/726 Rechenzeiten mit zwei Parametern, z.B. f (n, m) = O nm2 + n2 log m (falls f (n,m) nm2 +n2 log m . – Seite 94/726 Exkurs: Asymptotik bei kleinen Größen wie Wahrscheinlichkeiten p(n). ≤ c). f (n, m) polynomiell wachsend, wenn f (n, m) = O nk m k, l ∈ N. Im obigen Beispiel nm2 + n2 log m = O n2 m2 . l für Nach Definition: p(n) → 0 für n → ∞ ⇔ p(n) = o(1). Aber verschiedene Geschwindigkeiten: 1/ log log n wird sehr langsam klein: p(n) heißt polynomiell klein, wenn p(n) = O n−ε für ein ε > 0, ε exponentiell klein, wenn p(n) = O 2−n für ein ε > 0, echt exponentiell klein, wenn p(n) = O 2−εn für ein ε > 0. . – Seite 95/726 . – Seite 96/726 Wieder konkreter: Sei die Zeit für einen Rechenschritt 10−3 Sekunden. (Ich weiß, dass dies veraltet ist.) Wie ändert sich das Bild, wenn die Zeit für einen Rechenschritt um den Faktor 10 fällt ? [log(10p) = log p + log 10 ≈ log p, 3.162 ≈ 10, 2.153 ≈ 10, 23.3 ≈ 10] Maximale Eingabelänge bei Rechenzeit Tp (n) 1Sek. 1Min. 1Std. n 1000 60000 3600000 n log n 140 4895 204094 2 n 31 244 1897 3 n 10 39 153 n 2 9 15 21 Maximale Eingabelänge in Abhängigkeit der Technologie Tp (n) alt neu (d.h. 10-mal schneller) n p 10p n log n p (fast 10)p n2 p 3,16p 3 n p 2,15p n 2 p p + 3,3 . – Seite 97/726 2 Grundlegende Datenstrukturen . – Seite 98/726 Zunächst: Recherche in Literatur und Programmbibliotheken: 2.1 Motivation und Vorgehensweise – Gibt es schon passende Datenstruktur? Anwendungsproblem → Anforderungen an eine Datenstruktur: – Kann eine existierende Datenstruktur angepasst werden? – Neuentwicklung einer Datenstruktur unausweichlich? – Datentyp, Vorgehensweise in der Lehre ist anders. – Menge zu unterstützender Operationen, – Untersuchung aller schon bekannten Datenstrukturen daraufhin, welche Operationen sie unterstützen und welche nicht. – im Idealfall mit Häufigkeiten der Operationen oder Mindestanforderungen (z.B. Echtzeit oder realtime) – Abstrakte Anforderungen und Entwicklung passender Datenstrukturen, mögliche Anwendungen werden genannt. Wie wird das Problem gelöst? – Konkrete Anwendungen in Kapitel 5 oder anderen Vorlesungen. . – Seite 99/726 . – Seite 100/726 Was geht effizient? – Lese a(i) in O(1). 2.2 Arrays (veraltet auch Felder) – Ersetze a(i) durch x in O(1). Festgelegter, fortlaufender und begrenzter Bereich des Speichers. Es gibt n Speicherplätze für Daten eines vorgegebenen Typs. a(1) ... 1 a(i) ... i – Vertausche Daten an Positionen i und j in O(1). Was geht gar nicht? – Hinzufügen eines (n + 1) - ten Datums → neues Array aufbauen. (Entfernen kann durch Ersetzung durch Leerzeichen simuliert werden.) a(n) Was geht, aber nicht effizient? n i → a(i) in Zeit O(1), rechnerintern ist Startadresse N bekannt, so dass a(i) an Position N + i steht (Speicherabbildungsfunktion). – Entferne das Datum an Position j und schreibe es zwischen die Daten an den Positionen i und i + 1 → Zeit Ω(|i − j| + 1). – Entscheide, ob x im Array abgespeichert ist, kurz suche x → Zeit Ω(n). . – Seite 101/726 Suche in geordneten Arrays (a(1) ≤ a(2) ≤ · · · ≤ a(n)) . – Seite 102/726 Strategie 2: Binäre Suche Strategie 1: Lineare Suche i mittlere Position Vergleiche x mit a(i) Vergleiche x nacheinander mit a(1), a(2), . . . , bis – x = a(i) (x gefunden), – x < a(i) (x nicht im Array), x < a(i) – x > a(n) (x nicht im Array). Suche links von Position i worst case: Θ(n), auch typischer Fall. Aber empfehlenswert, wenn x vermutlich klein im Vergleich zu Arraydaten (→ Kap. 4). . – Seite 103/726 x = a(i) x gefunden x > a(i) Suche rechts von Position i Suchbereich leer → x nicht im Array . – Seite 104/726 Binäre Suche fährt rekursiv fort. Wie lange dauert es bei s Daten, bis Bereich leer? Mittlere Position im Bereich [l, r]: Größe des größeren Suchbereichs nach einem Vergleich l = 10, r = 20 → Position 15. l = 10, r = 21 → Position 15 oder 16. s Allgemein b(l + r)/2c (auch d(l + r)/2e möglich). (s − 1)/2 s ungerade s/2 s gerade bs/2c (Bemerkung: bbs/2c/2c = bs/4c.) Nach k Vergleichen größter Suchbereich hat Größe bn/2k c. bn/2k c = 0 ⇔ n < 2k ⇔ log n < k . Da k ganzzahlig, kleinster passender Wert für k : dlog(n + 1)e. → Binäre Suche in Zeit O(log n). . – Seite 105/726 . – Seite 106/726 Strategie 3: Geometrische Suche Phase 1: Vergleiche x mit den Daten an den Positionen 20 , 21 , 22 , . . . , 2i , . . . , bis Phase 2 – x gefunden, → Erfolg. i – Position 2 außerhalb → binäre Suche in [2i−1 + 1, n]. des Arrays, – x < a(2i ). → binäre Suche in [2i−1 + 1, 2i ]. Rechenzeit für Phase 1: ≤ dlog ne + 1 Vergleiche, Phase 2: ≤ dlog(n + 1)e − 1 Vergleiche. Also worst case O(log n) (ungefähr Faktor 2 langsamer als binäre Suche), Darstellung von Matrizen in Arrays Normale Matrizen (→ zweidimensional): n1 × n2 . Zeilenweise Abspeicherung, Speicherabbildungsfunktion (i, j) → (i − 1)n2 + j . Mehrdimensionale Matrizen: n1 × · · · × nk . Nacheinander i1 = 1, . . . , n1 nacheinander i2 = 1, . . . , n2 usw. Speicherabbildungsfunktion P (i1 , . . . , ik ) → (ij − 1) · 1≤j≤k−1 Q nm + i k . j<m≤k aber nur Zeit O(log i) für a(i). . – Seite 107/726 . – Seite 108/726 2.3 Lineare Listen Nötig ist: Unterprogramm zur Reservierung freier Speicherplätze. Listen sind dynamisch, Arrays sind statisch. Diese Begriffe beziehen sich darauf, ob die Anzahl der Daten festgelegt ist oder nicht. Wünschenswert (praktisch auch nötig): Garbage Collection. Algorithmen zur Einsammlung freigegebener Speicherplätze. → Vorlesung BS Dynamisch → kein festgelegter Speicherbereich. Unsere Darstellung a1 a2 a3 ... an Manchmal nützliche Erweiterungen (kosten aber Platz und Zeit für Updating): nil – Extrazeiger auf Listenende, Rechnerintern Unter L: Adresse von a1 . Bei ai : Adresse von ai+1 (oder nil als Markierung für Listenende). – Extrazeiger auf Vorgänger (doppelt verkettete Liste), – Extravariable für die Länge der Liste. . – Seite 109/726 Ein Vergleich von Arrays und linearen Listen Initialisierung Suche an Position p Suche nach Datum x Θ(n) bei Länge n Θ(1) Θ(n) (geordnete Arrays: Θ(log n)) O(1), aber Θ(n), wenn n Daten eingefügt werden Θ(p) Θ(l) bei Länge l . – Seite 110/726 Einfügen von y hinter x nach Suche von x nicht unterstützt Θ(1) Einfügen von y vor x nach Suche von x nicht unterstützt Θ(1), wenn bei Suche Vorgängerdatum abgespeichert Θ(1) bei doppelt verketteten Listen Entfernen von x nicht unterstützt, siehe oben evtl. Ersetzen durch Leerzeichen . – Seite 111/726 . – Seite 112/726 Ersetze x durch y nach Suche von x Θ(1) Θ(1) Bestimme Nachfolger Θ(1), wenn Position bekannt Θ(1) Θ(1), wenn Position bekannt Θ(l) Θ(1) bei doppelt verketteten Listen Bestimme Vorgänger Bestimme Anfangsdatum Θ(1) Bestimme letztes Θ(1), wenn Datum Arraygröße bekannt Θ(l) Θ(1) bei Extrazeiger Bestimme Länge fest vereinbart Θ(l) Θ(1) bei Extravariable Konkatenation nicht unterstützt Θ(l1 ), wenn l1 Länge der ersten Liste Θ(1) bei Extrazeiger auf letztes Datum Θ(1) . – Seite 113/726 Split hinter x nicht unterstützt, nach Suche von x aber prinzipiell in Θ(1) . – Seite 114/726 Θ(1) Topologisches Sortieren Gegeben: n Objekte und eine Liste paarweiser Beziehungen (x, y) für x 6= y . Stacks Θ(1) bei bekannter Maximalgröße Θ(1) Queues Θ(1) bei bekannter Maximalgröße und ringförmiger Verknüpfung Θ(1) bei Extrazeiger auf Listenende Gesucht: eine Reihenfolge z1 , . . . , zn der Objekte, so dass für i < j das Paar (zj , zi ) nicht in der Eingabe steht, oder die Aussage, dass eine solche Reihenfolge nicht existiert. Nun können wir entscheiden, ob Arrays oder Listen in einer gegebenen Situation günstiger sind. Beide unterstützen nicht Suchen, Einfügen und Entfernen → Kap. 3 . – Seite 115/726 . – Seite 116/726 Strukturelle Einbettung des Problems Anwendungen: Objekte sind Module eines Projektes (z. B. Hausbau) oder Programms, (x, y) besagt, dass x beendet sein muss, bevor y begonnen werden kann, gesucht eine durchführbare Reihenfolge der Module oder die Erkennung eines Deadlocks. Später: Objekte sind Knoten in gerichtetem Graph, (i, j) steht für eine Kante von i zu j , gesucht eine Nummerierung der Knoten, so dass Kanten von Knoten v nur zu Knoten mit höherer Nummer führen. Ergänze Paare um (x, x) und transitivem Abschluss, d. h. füge (x, z) hinzu, wenn es (x, y) und (y, z) bereits gibt. Interpretiere (x, y) als x ≤ y . Dann entsteht partielle Ordnung „≤“, wenn es keinen Deadlock gibt, ∀x : x ≤ x. ∀x, y : x ≤ y und y ≤ x ⇒ x = y (sonst Deadlock). ∀x, y, z : x ≤ y und y ≤ z ⇒ x ≤ z (Transitivität). anderes Beispiel einer partiellen Ordnung: Objekte: Teilmengen von {1, . . . , n}, ≤: Teilmengenbeziehung „⊆“. . – Seite 117/726 Eine partielle Ordnung ist sogar eine vollständige Ordnung, wenn für je zwei verschiedene Objekte x, y stets x ≤ y oder y ≤ x gilt. Partielle Ordnung auf Modulen eines Projektes meist keine vollständige Ordnung. Teilmengenbeziehung keine vollständige Ordnung, denn weder {1} ⊆ {2} noch {2} ⊆ {1}. Topologisches Sortieren ergänzt Beziehungen einer partiellen Ordnung zu einer vollständigen Ordnung (oder stellt fest, dass die Beziehungen gar keine partielle Ordnung beschreiben). . – Seite 118/726 Definition: Ein Objekt x heißt minimal, wenn es kein y 6= x mit y ≤ x gibt. „Algorithmus“: – Suche minimales Objekt. – Existiert keines, keine partielle Ordnung gegeben. – Ist xi minimal, xi an den Anfang der vollständigen Ordnung, entferne alle Paare (xi , ·) und starte neu, bis alle Objekte entfernt sind. Zur Korrektheit: Behauptung: Partielle Ordnung ⇒ Minimales Objekt existiert. Beweis durch Widerspruch Annahme: Kein Objekt ist minimal ⇒ zu x existiert f (x) 6= x mit f (x) ≤ x. . – Seite 119/726 . – Seite 120/726 Ein naiver Algorithmus – Durchlaufe alle Paare (x, y) mit x 6= y , zähle für jedes Objekt z , wie oft es an zweiter Stelle vorkommt (Array der Objekte). ≥ ≥ x0 := x ≥ x1 := f (x) ≥ x2 := f (x1 ) ≥ x3 ≥ x 4 x10 ≥ ≥ ≥ ≥ x8 x5 ≥ x9 – Schreibe alle z mit Zählwert 0 in die Ausgabe (in beliebiger Reihenfolge). x6 x7 Da Objektmenge endlich, muss ein Kreis xi , xi+1 , . . . , xj = xi entstehen. Es ist xi ≥ xi+1 ≥ · · · ≥ xj ≥ xi , also wegen Transitivität xi ≥ xi+1 und xi+1 ≥ xi . Nach Konstruktion xi+1 6= xi . Bedingung an partielle Ordnung: – Entferne alle Paare (z, ·), für die z in die Ausgabe kam. – Beginne von vorn, bis keine Paare mehr da sind. Widerspruch. xi ≤ xi+1 und xi+1 ≤ xi ⇒ xi = xi+1 . . – Seite 121/726 Ein schnellerer Algorithmus Preprocessing (Vorbereitung) Datenstrukturen: Array der Länge n für Integers, mit 0 initialisiert. Array L der Länge n für Listenanfänge, die leer initialisiert werden, Queue Q für Objekte, leer initialisiert. Durchlaufe alle Paare für (i, j) mit i 6= j erhöhe a(j) um 1, füge j in L(i) ein, entferne die Paare (i, j) mit i = j . Rechenzeit O(n + l) a(i) enthält die Anzahl aller (k, i) mit k ≤ i und k 6= i, L(i) enthält alle j 6= i, für die (i, j) in der Eingabe ist. . – Seite 123/726 Rechenzeit: O(nl), da mindestens ein Objekt in jeder Runde in die Ausgabe kommt und in jeder Runde maximal l Paare betrachtet werden. Wenn alle Paare (i, j) mit i < j die Eingabe bilden, Rechenzeit Θ(n3 ). . – Seite 122/726 Phase 1: Durchlaufe a und füge alle i mit a(i) = 0 (minimale Objekte) in Q ein. Phase 2: Solange Q nicht leer: Entferne vorderstes Element j aus Q, schreibe zj an die nächste Stelle der Ausgabe, durchlaufe L(j), für i ∈ L(j) senke a(i) um 1 (in der unverarbeiteten Restmenge hat zi einen Vorgänger weniger), falls a(i) den Wert 0 bekommt, füge i in Q ein (unter den Objekten, die noch nicht in der Ausgabe stehen, ist zi minimal). Rechenzeit O(n + l). Jede Liste einmal durchlaufen, jedes Objekt einmal in die Queue. . – Seite 124/726 Beispiel 2.3.5 i a(i) L(i) Wenn ein Objekt nicht in die Ausgabe kommt, gibt es kein minimales Objekt und die Eingabe beschreibt keine partielle Ordnung. Die Rechenzeit O(n + l) ist optimal, da – jedes Objekt in die Ausgabe kommt, 1 0 2 1 3 1 4 2 5 2 6 2 7 2 8 2 9 0 3 8 7 6 8 nil 5 6 4 nil nil nil nil nil 4 nil 7 – jedes Paar bei vielen Eingaben gelesen werden muss, z. B. für die Eingabe aller (i, j) mit i < j bei der Reihenfolge der Paare gemäß fallendem d := j − i. nil 5 2 Q : nil a(1) = a(9) = 0 ⇒ Q : 1, 9 . – Seite 125/726 Addiere die i-ten Zeilen nach dem Reißverschlussverfahren: Nur vage definiert als Matrizen M mit sehr vielen Nullen. Darstellung der i-ten Zeile als Liste L(i) der von Null verschiedenen Elemente und ihrer Position: (j, M (i, j)), manchmal auch (i, j, M (i, j)). Beispiel 2.3.6 0 0 0 4 0 0 0 0 0 0 0 0 2 0 0 0 0 1 0 0 (1,0,0) (2,0,0) (3,0,0) (4,0,0) (5,0,0) (1,1,5) nil (3,1,4) (4,2,4) nil nil (3,4,2) nil . – Seite 126/726 Addition der spärlich besetzten Matrizen A und B (mit a bzw. b von Null verschiedenen Elementen und n Zeilen) Spärlich besetzte Matrizen (sparse matrices) 5 0 4 0 0 nil (3,5,1) nil . – Seite 127/726 Starte am Anfang von LA (i) und LB (i) und erzeuge LC (i) für C = A + B. Wenn (j, A(i, j)) und (k, B(i, k)) erreicht sind: j < k : Hänge (j, A(i, j)) an das Ende von LC und suche in LA Nachfolger auf. j > k : Hänge (k, B(i, k)) an das Ende von LC und suche in LB Nachfolger auf. j = k und A(i, j) + B(i, k) 6= 0 : Hänge (j, A(i, j) + B(i, k)) an das Ende von LC und suche in LA und in LB den Nachfolger auf. j = k und A(i, j) + B(i, k) = 0 : Suche in LA und in LB den Nachfolger auf. . – Seite 128/726 Wenn in LA oder LB Listenende erreicht, kann der Rest der anderen Liste kopiert und an LC angehängt werden. Wenn beide Listenenden erreicht sind, stoppe Konstruktion dieser Zeile von LC . Multiplikation spärlich besetzter Matrizen Für C(i, j) Skalarprodukt der i-ten Zeile von A und der j -ten Spalte von B mit Reißverschlussverfahren. Rechenzeit für alle Zeilen: O(n + a + b). . – Seite 129/726 . – Seite 130/726 Benutze gemeinsame Listen für Zeilen und Spalten. Wo kommen spärlich besetzte Matrizen vor? (0,1,0) (1,0,0) (0,2,0) (0,3,0) (0,4,0) (0,5,0) (1,1,5) nil – Numerik, – Adjazenzmatrizen von Graphen, (2,0,0) (3,0,0) nil (3,4,2) (3,1,4) (4,0,0) (3,5,1) (4,2,4) – Übergangsmatrizen von Markovketten. nil Allgemein existieren oft nur wenige von vielen möglichen Paarbeziehungen. nil (5,0,0) nil nil nil nil nil nil . – Seite 131/726 . – Seite 132/726 2.4 Datenstrukturen für Mengen Alternative 1 : Bitvektordarstellung oder charakteristischer Vektor. Voraussetzung: Alle Mengen sind Teilmengen eines endlichen bekannten Universums (Grundmenge) U = {1, . . . , n}. Darstellung von M : Array aM der Länge n für Bits mit der Interpretation: i ∈ M ⇔ aM (i) = 1. i ∈ M? M := M ∪ {i} M := M − {i} aM (i) aM (i) := 1 aM (i) := 0 O(1). O(1). O(1). Operationen auf zwei Mengen können bitweise ausgeführt werden: Vereinigung OR Durchschnitt AND Differenz NEGIERTE IMPLIKATION a1 (i) AND (NOT a2 (i)) Symmetrische Differenz EXOR Rechenzeit Θ(n) auch für Mengen A, B mit |A|, |B| n. Speicherplatzersparnis: w Bits bilden ein Wort und passen in einen Arrayplatz. Oft: bitweise Operationen auf Wörtern in O(1). Zeit fällt von Θ(n) auf Θ(n/w). Wo finden wir aM (i)? Im di/we-ten Wort an Position (i mod w), wobei Positionen 0, . . . , w − 1. . – Seite 133/726 Fazit: Effizient, wenn Mengen typischerweise im Verhältnis zu n nicht sehr klein sind. . – Seite 134/726 Einzige Option: Suche jedes Element aus Liste L1 in L2 : O(l1 · l2 ) Geordnete Listen der Länge l1 und l2 . Alternative 2: ungeordnete oder geordnete Listen. Suchen, Einfügen und Entfernen bereits behandelt. Für alle Mengenoperationen kann das Reißverschlussverfahren benutzt werden: O(l1 + l2 ). Ungeordnete Listen der Länge l1 und l2 am Beispiel der Vereinigung: Bitvektor ungeordnete Liste geordnete Liste O(1) O(l) O(l) INSERT O(1) O(1) O(1) DELETE O(1) O(1) O(1) Vereinigung O(n) bzw. O(n/w) O(l1 l2 ) O(l1 + l2 ) Durchschnitt O(n) bzw. O(n/w) O(l1 l2 ) O(l1 + l2 ) Differenz O(n) bzw. O(n/w) O(l1 l2 ) O(l1 + l2 ) Symm. Diff. O(n) bzw. O(n/w) O(l1 l2 ) O(l1 + l2 ) i ∈ M? – Hänge L2 an L1 in Zeit O(1) bei Extrazeiger auf Listenende. Gravierender Nachteil: Elemente können mehrfach abgespeichert werden, nur effizient bei garantiert disjunkten Mengen (→ Kap. 2.8). Weiterer Nachteil: Kein Pendant für die anderen Operationen wie Durchschnitt. . – Seite 135/726 . – Seite 136/726 2.5 Datenstrukturen für Bäume Wann ist T = (V, E, r) ein gewurzelter Baum? – Es gibt keine Kante (·, r), ind(r) = 0 Anwendungen: überall. Gewurzelte Bäume T = (V, E, r). r v1 v4 v7 v3 v2 v5 Knotenmenge V = {r, v1 , . . . , v12 }. Kantenmenge E = {(r, v1 ), (r, v2 ), (r, v3 ), . . . , (v9 , v12 )}. Gerichtete Kanten sind Paare verschiedener Knoten. v6 v8 v9 v10 v11 v12 v8 und v9 sind Kinder von v6 . v6 ist Elter von v8 und v9 . v8 und v9 sind Geschwister. r ist Wurzel des Baumes. r, v1 , v2 , v4 , v6 , v9 sind innere Knoten. v3 , v5 , v7 , v8 , v10 , v11 , v12 sind Blätter. . – Seite 137/726 – ∀v 6= r: Es gibt genau eine Kante (·, v), ind(v) = 1 Kantenzahl |V | − 1 – ∀v 6= r: Es gibt einen Weg von r zu v , d.h. es gibt vi0 , . . . , vim mit vi0 = r, vim = v und (vij , vij+1 ) ∈ E . (Die Länge des Weges beträgt dann m.) Da Elter eindeutig, gibt es für v, w ∈ V maximal einen Weg von v zu w. Unterscheidung in direkte Vorgänger und Vorgänger, direkte Nachfolger und Nachfolger. Tiefe(v) := Länge des Weges von r zu v . Tiefe(T ) := max {Tiefe(v)|v ∈ V }. . – Seite 138/726 outd(v ) := Anzahl der Kinder von v . v Blatt :⇔ outd(v ) = 0. Traversierung von Bäumen Baum outd-k-beschränkt :⇔ ∀v ∈ V : outd(v ) ≤ k. Baum k-är (1-är = unär = lineare Liste, 2-är = binär, 3-är = ternär) :⇔ ∀v ∈ V : outd(v )= 0 oder outd(v )= k . Ziel: Durchlaufe effizient alle Knoten des Baumes. Für verschiedene Zwecke drei Strategien, rekursiv definiert: r geordnet Ein k-ärer Baum heißt vollständig :⇔ Alle Blätter haben dieselbe Tiefe d. v1 . . . vn (→ Anzahl der Knoten 1 + k + · · · + k d = (k d+1 − 1)/(k − 1).) Geordnete Bäume: Kinder sind angeordnet, besonders bei binären Bäumen linkes und rechtes Kind. Zu v ∈ V gehört der Teilbaum T (v) mit v als Wurzel und allen von v aus erreichbaren Knoten. post(T ) := post(T (v1 )),. . . ,post(T (vk )),r. pre(T ) := r, pre(T (v1 )),. . . ,pre(T (vk )). in(T ) := in(T (v1 )),r,in(T (v2 )),. . . ,in(T (vk )) (typischerweise bei binären Bäumen). (Jeweils in Zeit O(n) für n = |V | realisierbar.) . – Seite 139/726 . – Seite 140/726 Operationen auf gewurzelten Bäumen Parent-Array PARENT(x, T ), CHILD(x, i, T ), LCHILD(x, T ) und RCHILD(x, T ) bei binären Bäumen, ROOT(T ), DEPTH(x, T ), DEPTH(T ), SIZE(T ), CONCATENATE(x, T1 , . . . , Tm ), wobei ROOT(T1 ),. . . , ROOT(Tm ) gegeben: Typischerweise V = {1, . . . , n}. Array der Länge n, Eintrag an Position i: PARENT(i). x T1 ... Tm Unterstützt werden PARENT O(1), ROOT O(depth(T )), DEPTH für einzelne Knoten O(depth(i)), CONCATENATE O(m), aber nicht CHILD, Gesamttiefe. x −→ T1 ... Sehr wenig Speicherplatz, gibt nicht die Idee der Kantenrichtungen wieder, diese zeigen eher in Richtung der Wurzel. (→ Kap. 2.8) Tm Häufig ROOT(T ) und SIZE(T ) in Extravariablen. . – Seite 141/726 . – Seite 142/726 Array von Child-Listen Wie gut ist die Speicherplatzauslastung? V = {1, . . . , n}. Array der Länge n, Position enthält Zeiger auf lineare Liste der Kinder von i. Z. B. Maximalzahl der Kinder k . Arrays der Länge k + 1 für Elter und Kinder. Unterstützt werden CONCATENATE O(m) und CHILD O(#Kinder). Gesamtzahl der Arrayplätze: n(k + 1). Jede Kante wird zweimal dargestellt, also 2(n − 1) belegte Arrayplätze. Prozentuale Ausnutzung der Plätze: Wenn Anzahl der Kinder oder Maximalzahl bekannt, ersetze Liste durch Arrays. 2n − 2 2 2 2 = − < . n · (k + 1) k + 1 n · (k + 1) k+1 Fazit: Speichere ROOT und SIZE in Extravariablen, ggf. kombiniere Parent-Array und Array von Child-Listen oder Child-Arrays. Für k = 1 doppelt verkettete Liste, bei binären Bäumen gute Speicherplatzausnutzung. . – Seite 143/726 . – Seite 144/726 A Lassen sich allgemeine Bäume durch binäre Bäume simulieren? B Ja, linkes Kind := linkestes Kind, rechtes Kind := rechtes Geschwisterteil. E A C F −→ D G H I nil B J E nil nil F C G D nil nil H nil nil I nil J nil nil Suche nach i-tem Kind: left und (i − 1)-mal right. . – Seite 145/726 . – Seite 146/726 1 2.6 Datenstrukturen für Graphen Anwendungen überall. Ungerichtete Kanten drücken symmetrische Beziehungen aus, gerichtete Kanten auch asymmetrische Beziehungen. V endliche Knotenmenge (vertices, nodes), n := |V |, E Kantenmenge (edges), m := |E|. Ungerichtete Kante {v, w}, gerichtete Kante (v, w). 1 2 4 5 3 6 2 7 4 5 3 6 7 G = (V, E) wenn Verwechslungen ausgeschlossen sind, auch (v, w). v und w adjazent, wenn zwischen ihnen eine Kante verläuft. v und e inzident, wenn v Endknoten von e. . – Seite 147/726 8 8 d(v) Grad (degree) = Anzahl inzidenter Kanten. d(5) = 3 ind(v ) Ingrad = Anzahl eingehender Kanten. outd(v ) Outgrad = Anzahl ausgehender Kanten. ind(5)= 1, outd(5)= 2 (1,2,5,7,3) ungerichteter Weg (1,2,3,7,5) gerichteter Weg der Länge 4. der Länge 4. . – Seite 148/726 Wege heißen einfach, wenn höchstens Anfangs- und Endknoten übereinstimmen. Kreise sind einfache Wege mit Anfangsknoten = Endknoten und Länge > 0 für gerichtete Graphen und Länge > 2 für ungerichtete Graphen. Graphen ohne Kreise heißen azyklisch. Ungerichtet Gerichtet u ≈ v :⇔ u ≈ v :⇔ ∃ Weg zwischen u und v . ∃ Weg von u nach v und ∃ Weg von v nach u. – ∀v ∈ V : v ≈ v . – ∀u, v ∈ V : u ≈ v ⇒ v ≈ u. – ∀u, v, w ∈ V : u ≈ v, v ≈ w ⇒ u ≈ w. Also ist ≈ in beiden Fällen eine Äquivalenzrelation ⇒ V zerfällt in disjunkte Äquivalenzklassen, d. h. V = V1 ∪˙ · · · ∪˙ Vk , alle Knoten in Vi sind äquivalent, aber keine Knoten aus Vi und Vj sind äquivalent, falls i 6= j . . – Seite 149/726 . – Seite 150/726 Ungerichtete Graphen: Äquivalenzklassen heißen Zusammenhangskomponenten (connected components), sie sind die nicht vergrößerbaren Mengen von paarweise verbundenen Knoten. Wenn wir Kanten (i, j) als i ≤ j interpretieren, bilden genau die azyklischen Graphen eine partielle Ordnung. Deren Knotenmenge kann topologisch sortiert werden. (s. Kap. 2.3). Im Beispiel: V1 = {1, 2, 3, 4, 5, 7}, V2 = {6, 8}. Adjazenzmatrix A: A(i, j) = 1, falls (i, j) ∈ E (gerichtet) oder {i, j} ∈ E (ungerichtet), A(i, j) = 0 sonst. Zusammenhängende, azyklische, ungerichtete Graphen heißen auch Bäume. Speicherplatz für n2 Bits (Arraydarstellung möglich). Gerichtete Graphen: Äquivalenzklassen heißen starke Zusammenhangskomponenten (strongly connected components), sie sind die nicht vergrößerbaren Mengen von Knoten, so dass es von jedem Knoten zu jedem anderen einen gerichteten Weg gibt. Adjazenzlisten: Im Beispiel: V1 = {1, 2, 3, 5, 7}, V2 = {4}, V3 = {6}, V4 = {8}. . – Seite 151/726 Array der Knoten, für Knoten i Liste der j mit (i, j) ∈ E (gerichtet) oder {i, j} ∈ E (ungerichtet). Speicherplatz n für das Array und m bzw. 2m Listeneinträge. . – Seite 152/726 Tiefensuche in ungerichteten Graphen Ist (i, j) ∈ E ? Adjazenzmatrix O(1), Adjazenzlisten O(d(i)). Berechne d(i) Θ(n) Θ(d(i)). Meistens Darstellung durch Adjazenzlisten. Traversieren von Graphen und Erzeugung einer nützlichen Kantenpartition Tiefensuche (DFS, depth first search) und Breitensuche Informal: Versuche einen Weg so lang wie möglich auszudehnen, ohne Kreise zu schließen. Wenn es nicht weitergeht, Backtracking, neuer Start am Vorgänger (← Stack für aktuellen Weg). Kanteneinteilung: Baumkanten (Treekanten) erreichen Knoten erstmalig, alle anderen Rückwärtskanten (Backkanten), da sie im Baum zurückführen. DFS(v), gestartet bei leerem Stack, erreicht alle w mit v ≈ w und ordnet ihnen eine DFS-Nummer zu. (BFS, breadth first search). Es wird also die Zusammenhangskomponente von v bearbeitet, d. h. Neustart bei allen anderen Knoten nötig: . – Seite 153/726 Datenstrukturen: Graph beschrieben durch Adjazenzlisten. Array P (Predecessor) für T -Vorgänger, mit Nullen initialisiert. Array num der Länge n für DFS-Nummern, mit Nullen initialisiert. Listen für die Mengen T (Treekanten) und B (Backkanten), zu Beginn leer. Variable i für Integers, zu Beginn 0. → O(n) Rahmenprogramm: Für x ∈ V : if num(x) = 0 then P (x) := 0 und DFS(x). . – Seite 154/726 DFS(v): i := i + 1, num(v) := i. (Die nächste freie Nummer wird an v vergeben.) w noch nicht gelesen Durchlaufe Adj(v) ↓ für w:Falls num(w) = 0, (v, w) → T, P (w) := v , DFS(w). Falls num(w) 6= 0 und w = P (v), tue nichts. → es ist (w, v) in T Falls num(w) 6= 0, num(w) < num(v) und w 6= P (v), (v, w) → B . Falls num(w) 6= 0, num(w) > num(v), tue nichts. Die Kanten in T und B sind gerichtet! . – Seite 155/726 . – Seite 156/726 T - Kanten 1 2 8 Adj(1) 3 2 2 B -Kanten Analyse: ignoriert 1 1 – Jede Kante {v, w} wird genau einmal von v und genau einmal von w betrachtet (→ O(n + m)). 3 3 4 7 3 2 Adj(2) 5 13 4 5 7 6 4 7 7 6 6 – Jede Kante {v, w} wird in genau einer Richtung entweder T - oder B -Kante. 4 5 Adj(3) 125 7 5 9 8 2 Adj(4) 8 Adj(5) 23 Neustart 8 Adj(6) Adj(7) – Die T -Kanten bilden einen Wald (Menge von Bäumen), dessen Bäume die Knoten der Zusammenhangskomponenten zusammenfassen. Stop 23 6 Adj(8) . – Seite 157/726 Jeder DFS(v)-Aufruf gibt DFS-Nummer an v , danach kein neuer DFS(v)-Aufruf. Jede Adjazenzliste wird genau einmal durchlaufen (→ Behauptung 1). Sei {v, w} ∈ E , o. B. d. A. wird v früher erreicht → DFS(v) startet → w wird über {v, w} erstmals gefunden → (v, w) ∈ T . → w wird anders gefunden, dann num(w) > num(v). . – Seite 158/726 DFS(w) innerhalb von DFS(v), also wird v in Adj(w) vor w in Adj(v) gefunden → (w, v) ∈ B , da v 6= P (w) und num(v) < num(w). Später wird w in Adj(v) gefunden → (v, w) 6∈ B , da num(v) < num(w) (→ Behauptung 2). Knoten bei Neustart im Rahmenprogramm erhält keinen T -Vorgänger. Alle anderen erhalten genau einen T -Vorgänger. Die T -Kanten sind azyklisch, da (v, w) ∈ T ⇒ num(v) < num(w) (→ Behauptung 3). Ungerichtete Graphen sind genau dann azyklisch, wenn DFS keine B -Kante erzeugt. . – Seite 159/726 . – Seite 160/726 Schematisierung des zeitlichen Ablaufs: Wenn DFS(w) vor DFS(v) begonnen wird, wird DFS(w) später als DFS(v) beendet. Tiefensuche in gerichteten Graphen Informal: T -Kanten wie bisher, bei Start in v kommen alle von v aus erreichbaren Knoten in den T -Baum mit Wurzel v . Weitere Einteilung: B -Kanten führen im Baum zu einem Vorgänger. F -Kanten (Forward) führen im Baum zu einem Nachfolger. C -Kanten (Cross) sind die restlichen Kanten, sie führen innerhalb eines Baumes zu einem früheren Teilbaum oder zu einem früher konstruierten Baum. DFS(v ) w in Adj(v ) gefunden Möglichkeiten und Klassifikation von DFS(w ) unmöglich, da v in Adj(w ) v in Adj(w) gefunden → (w, v) ∈ B → (w, v) ∈ T → (v, w) ∈ B → (v, w) ∈ T . – Seite 161/726 Formal: 0 α(v) = 1 2 bis DFS(v) begonnen wird (äquivalent num(v) = 0), während DFS(v), nach Beendigung von DFS(v). Es wird w in Adj(v) gefunden. (1) num(w) = 0 → (v, w) T -Kante. (2) num(w) 6= 0, num(w) > num(v) → (v, w) F -Kante, da w innerhalb DFS(v) entdeckt wurde. (3) num(w) 6= 0, num(w) < num(v), α(w) = 1 → (v, w) B -Kante, da v innerhalb DFS(w) entdeckt wurde. (4) Sonst, d. h. num(w) 6= 0, num(w) < num(v), α(w) = 2 → (v, w) C -Kante, da v nach Beendigung von DFS(w) entdeckt wurde. . – Seite 163/726 . – Seite 162/726 7 4 7 4 2 3 3 1 1 6 5 13 5 12 6 10 11 8 9 9 8 14 2 Adj(1) 3 8 Adj(2) Adj(3) Adj(4) Adj(5) 4 5 9 7 17 7 3 13 Adj(6) Adj(7) Adj(8) 19 Adj(9) 6 Neustart Jede Kante wird einmal verarbeitet → O(n + m). . – Seite 164/726 Breitensuche Schematisierung des zeitlichen Ablaufs für (v, w) Informal: Vom Startpunkt aus werden alle w ∈ Adj(v) zuerst nummeriert und in eine Queue geschrieben. Solange die Queue nicht leer ist, wird BFS für das nächste Element der Queue aufgerufen. DFS(v) w in Adj(v) gefunden Möglichkeit für DFS(w) (v, w) ∈ C (v, w) ∈ B (v, w) ∈ F 10 unmöglich. Wenn DFS(w) später als DFS(v) beginnt, wird DFS(w) nicht durch DFS(v) unterbrochen. (v, w) ∈ T 13 11 5 6 2 12 7 8 3 4 9 1 Die Knoten werden in der Reihenfolge ihres Abstandes vom Startknoten gefunden. O(n + m) . – Seite 165/726 . – Seite 166/726 Zwei naive Lösungen 2.7 Datenstrukturen für Intervalle Grundmenge {1, . . . , n}. Intervalle sind Teilmengen vom Typ {i, . . . , j}, 1 ≤ i ≤ j ≤ n. Sie spielen eine besondere Rolle, z. B. bei Bildverarbeitung, Geometrie, Genomsequenzen in der Molekularbiologie, . . . Problemstellung: n fest vorgegeben, i → wi , „◦“ eine assoziative Verknüpfung wie „+“, „∗“, „min“, . . . hier o. B. d. A. „+“. Unterstützt werden sollen: query(i, j) → w(i, j) := wi + · · · + wj . update(i, a) → je nach Bedarf wi := a oder wi := wi + a. . – Seite 167/726 – Array der Länge n für wi an Position i. Platz Θ(n), update Zeit O(1), query Zeit O(n), preprocessing Zeit (Vorbereitung der Datenstruktur) O(n). Die meisten Intervalle erfordern query Zeit Θ(n). – Matrix der Größe n × n für w(i, j) an Position (i, j), i ≤ j , 0 sonst. Platz Θ(n2 ), update Zeit Θ(n2 ), query Zeit O(1), preprocessing Zeit Θ(n2 ). Beachte: i ist in i · (n − i + 1) Intervallen enthalten. . – Seite 168/726 Level k : Abstand k von der Wurzel. Segmentbäume (segment trees) allgemein [l, r] [1,20] [l, b(l + r)/2c] [11,20] [1,10] [1,5] [6,10] [11,15] [b(l + r)/2c + 1, r] [16,20] (Erinnerung ddn/2e/2e = dn/4e, bbn/2c/2c = bn/4c, also bn/4c ≤ dbn/2c/2e, bdn/2e/2c ≤ dn/4e ). Also Intervalle auf Level k haben bn/2k c oder dn/2k e Elemente. Falls dn/2k e ≥ 2, wird das größte Intervall halbiert. Das größte k mit dn/2k e ≥ 2 ist k = dlog ne − 1. [1,3] [4,5] [6,8] [9,10] [11,13] [14,15] [16,18] Also: Tiefe des Segmentbaums für [1, n] ist dlog ne. [19,20] [1,2] [3,3] [4,4] [5,5] [6,7] [8,8] [9,9] [10,10] [11,12] [13,13] [14,14] [15,15] [16,17] [18,18] [19,19] [20,20] [1,1] [2,2] [6,6] [7,7] [11,11] [12,12] [16,16] [17,17] . – Seite 169/726 Binäre Bäume mit n Blättern haben n − 1 innere Knoten und somit 2n − 1 Knoten und 2n − 2 Kanten. Induktionsbeweis: n = 1 (klar). n − 1 → n: Sei T binärer Baum mit n Blättern, v und w Geschwisterblätter. Preprocessing – Baue Baum ohne w(i, j)-Werte top-down, z. B. Inorder. – Speichere wi am Blatt [i, i]. – Berechne alle w(i, j) mit Postorder-Traversierung. O(n) u v . – Seite 170/726 w Entferne v und w → u wird Blatt → n − 1 Blätter. Rest hat (Indvss.): n − 2 innere Knoten, also hat T n − 1 innere Knoten, n Blätter, zusammen 2n − 1 Knoten und 2n − 2 Kanten, da Ingrad 1 für alle Knoten mit Ausnahme der Wurzel. Speicherplatz O(n). . – Seite 171/726 . – Seite 172/726 Update (i, a) query (i, j) 1. Ansatz – Starte an der Wurzel und suche das Blatt [i, i], speichere Weg auf einem Stack. O(log n) – Ändere den Wert an [i, i]. O(1) – Gehe den Weg rückwärts und berechne die Werte auf diesem Weg neu (alle anderen Werte bleiben aktuell). O(log n) [k, l] w1 v2 v20 w2 v3 w30 w3 v4 v40 w40 w4 v5 0 w50 w5 v6 v6 w6 [i, i] [j, j] v1 Suche Wege zu [i, i] und [j, j], speichere die Wege ab dem Gabelungspunkt [k, l]. O(log n) . – Seite 173/726 Stets gilt: [i, j] ⊆ [k, l] . – Seite 174/726 Verwende schließlich die Information an [i, i]. [k, l] v1 . . . . . . . [i, i] v2 [j, j] T (v1 ) Weg zu [i, i] geht nach links → Verwende Informationen an w. u v Auf dem Weg zu [j, j] spiegelbildlich analog. Jedes wm mit i ≤ m ≤ j wird genau einmal verwendet. Wege zu [i, i] und [j, j] enthalten zusammen max. 2dlog ne + 1 Knoten. Pro Ebene wird auf jedem Weg maximal ein Extraknoten betrachtet. Arbeit pro Knoten: O(1), also insgesamt O(log n). w u v Alle Intervalle [a, b] in T (v1 ) haben die Eigenschaft b ≤ j . Falls zusätzlich i ≤ a, können sie für w(i, j) benutzt werden. w Weg zu [i, i] geht nach rechts → Verwende Informationen an v nicht. . – Seite 175/726 . – Seite 176/726 query(i, j) Starte mit (k, l) := (1, n). Ansatz 2 Ansatz 1 + Verbesserung: Ist an der Gabelung [k, l] = [i, j], verwende Information dort und stoppe. Wird in T (v1 ) ein Intervall [a, b] mit a = i erreicht, verwende Information dort und stoppe. Phase 1 Fall 1 j ≤ b(k + l)/2c, Gabelungspunkt noch nicht erreicht, nur links suchen, l := b(k + l)/2c. Fall 2 i ≥ b(k + l)/2c + 1, Gabelungspunkt noch nicht erreicht, nur rechts suchen, k := b(k + l)/2c + 1. Fall 3 (i, j) = (k, l). Ausgabewert steht an diesem Knoten. STOP. Fall 4 NOT(Fall 1 OR Fall 2 OR Fall 3), d. h. i ≤ b(k + l)/2c < j , (k, l) 6= (i, j) Gabelungspunkt erreicht. Starte Phase 2 mit (k, b(k + l)/2c) und Phase 3 mit (b(k + l)/2c + 1, l). . – Seite 177/726 Phase 2 Stets gilt i ≤ k ≤ l ≤ j . Fall 1 i ≥ b(k + l)/2c + 1, alle Informationen dieses Teilbaumes im rechten Teilbaum, k := b(k + l)/2c + 1. Fall 2 i = k , benutze w(k, l) für Gesamtergebnis, STOP. Fall 3 i > k , aber nicht Fall 1, benutze alle Informationen im rechten Teilbaum, also w(b(k + l)/2c + 1, l), für das Gesamtergebnis und suche Restinformationen im linken Teilbaum, l := (b(k + l)/2c). . – Seite 178/726 Zusammenfassung alle wi Speicherplatz Θ(n) Preprocessing Θ(n) update(i, a) Θ(1) query(i, j) O(n) alle Intervalle Segmentbaum Θ(n2 ) Θ(n) Θ(n2 ) Θ(n) 2 O(n ) Θ(log n) Θ(1) O(log n) Phase 3 Spiegelbildlich analog. . – Seite 179/726 . – Seite 180/726 2.8 Datenstrukturen für Partitionen Grundmenge {1, . . . , n}. Zu jedem Zeitpunkt gehört i genau einer Teilmenge an, einer Firma, einer Zusammenhangskomponente, . . . Es gibt aber Fusionen. Zu unterstützende Operationen: FIND(i) → Name der Teilmenge, in der sich i zu diesem Zeitpunkt befindet. UNION(A,B) → Vereinigung der Mengen A und B; Name der Vereinigungsmenge frei wählbar, solange die Namen aktueller Mengen verschieden sind, A und B werden eliminiert. Zwei naive Lösungen – Array der n Elemente, an Position i der Name der Menge, die i enthält. Speicherplatz O(n), Preprocessing O(n), FIND O(1), UNION O(n). Sequenz von m FIND- und n − 1 UNION-Befehlen O(n2 + m). . – Seite 181/726 . – Seite 182/726 Datenstrukturen mit besonders schnellen FINDs – Array für n Listenanfänge, die die aktuellen Elemente der Menge enthalten, zusätzlich SIZE-Wert für jede Liste, zu Beginn: Menge i enthält genau i, UNION: kleinere Menge auflösen, Liste durchlaufen und Elemente in Liste der größeren Menge einfügen. SIZE-Werte addieren. Speicherplatz O(n), Preprocessing O(n), FIND O(n) UNION O(min(size(A), size(B))). Sequenz von m FIND- und n − 1 UNION-Befehlen O(nm + Zeit für UNION). Kombiniere die beiden naiven Datenstrukturen 3 1 7 3 3 1 7 3 1 1 3 7 Array mit FIND(i) 4 0 5 0 0 0 3 0 0 0 0 0 Array der Mengen mit SIZE-Variable Speicherplatz O(n) 2 nil 5 nil nil nil 12 nil nil nil nil nil 6 4 3 10 11 7 9 1 nil nil 8 Preprocessing O(n) FIND O(1) nil . – Seite 183/726 . – Seite 184/726 UNION(A, B) 1 2 3 – Teste, ob size(A) ≤ size(B). (Im Folgenden nur der Fall „Ja“, der andere Fall analog.) – Durchlaufe L(A): für alle i: FIND(i) := B , entferne i aus L(A), füge i in L(B) ein. Kosten nur O(n) 4 .. . n n-1 n=2k 1 2 3 4 ...... – size(B) = size(A) + size(B), size(A) := 0. balancierter Baum O(size(A)) Gesamtkosten → n/2 UNION, Kosten 1 → n/4 UNION, Kosten 2 → n/2 → n/2 → n/2i UNION, Kosten 2i−1 → n/2 → n/2k UNION, Kosten 2k−1 = n/2 → n/2 m FIND-Befehle und n − 1 UNION-Befehle: O(m + Kosten UNIONs}) | {z (n/2) · log n = O(n log n). O(n2 ) bessere Abschätzung? . – Seite 185/726 . – Seite 186/726 Kosten der UNION-Befehle O(n log n) Seien A1 , . . . , An−1 die jeweils kleineren Mengen der UNION-Befehle → Kosten |A1 | + · · · + |An−1 |, Anzahl der Summanden: n − 1, Größe der Summanden: mind. 1, max. n/2, schwer abzuschätzen. Ausweg: Buchhaltermethode Buche die Kosten auf andere Kostenträger um, addiere dann. Wie viele Kosten kann ein Kostenträger k maximal erhalten? Kosteneinheit an k → k bei Vereinigung in der kleineren Menge → die neue Menge ist mindestens doppelt so groß → bei s Kosteneinheiten enthält die zugehörige Menge am Ende mindestens 2s Elemente → Neue Kostenträger: 1, . . . , n. i-ter UNION-Befehl mit Kosten |Ai | gibt je eine Kosteneinheit an j ∈ Ai . 2s ≤ n → s ≤ blog nc → Gesamtkosten ≤ n · blog nc. . – Seite 187/726 . – Seite 188/726 Initialisierung: Datenstruktur für besonders schnelle UNIONs i in Menge i, d. h. A(i) = (nil, i, 1) M (i) = i Mengenname Nummer der Wurzel der Menge i Mengen sind Bäume mit Zeigern auf Elter. Mengengröße Speicherplatz O(n) Preprocessing Zeit O(n) UNION(i, j ) – M (i) und M (j) berechnen, – sei A(M (i)) = (nil, i, s1 ), A(M (j)) = (nil, j, s2 ), Mengenname und -größe Array A für die Elemente 1, . . . , n, für i Verweis auf Elter, falls nil, Mengenname und -größe. Array M für die Mengennamen aus 1, . . . , n, für j entweder nil für ungültig, oder auf den Knoten, der der Wurzel der Menge entspricht. – falls s1 ≤ s2 : A(i) := j , M (i) := nil A(j) := (nil, j, s1 + s2 ), M (j) := j sonst analog. O(1) . – Seite 189/726 . – Seite 190/726 FIND(i) – Starte an i und suche über die Elterzeiger die Wurzel, wo der Mengenname steht. O(Tiefe des Baumes) Lemma: Bei n Daten haben die Bäume maximal Tiefe blog nc. Beweis: Behauptung: Tiefe d ⇒ mind. 2d Knoten. d − 1 → d: Sei T ein Baum mit Tiefe d und kleinster Knotenzahl. Wie entstand T ? Also size(T1 ) ≤ size(T2 ) < size(T1 )+ T1 T2 size(T2 ) = size(T ). T depth(T1 ) ≤ d − 1, da depth(T ) = d. Bei Tiefe blog nc + 1 wären das mindestens 2blog nc+1 > n Knoten, Widerspruch. Falls depth(T1 ) < d − 1, dann depth(T2 ) = d und T2 hat Tiefe d und weniger Knoten als T , Widerspruch. Beweis der Behauptung durch Induktion über d. Somit depth(T1 ) = d − 1 und (Indvss.) size(T1 ) ≥ 2d−1 . Auch size(T2 ) ≥ size(T1 ) ≥ 2d−1 und size(T ) = size(T1 )+ size(T2 ) ≥ 2d−1 + 2d−1 = 2d . d = 0 : Tiefe 0 → 1 Knoten X . – Seite 191/726 . – Seite 192/726 v0 Effizienzsteigerung durch Pfadkomprimierung (path compression) v0 v1 Etwas mehr Aufwand bei FIND-Operationen als nötig, um spätere FIND-Operationen zu beschleunigen. v1 T0 T0 v2 T1 T1 T2 Tk vk = x T2 FIND(i) vk = x v2 Tk – Starte an i und suche über den Elterzeiger die Wurzel, wo der Mengenname steht. Speichere alle dabei gefundenen Knoten (z. B. Stack). – Wähle für alle Knoten auf dem Suchpfad die Wurzel als neuen Elter. Aufwand pro Knoten in etwa verdoppelt, aber Suchwege für viele Knoten verkürzt. Analyse? Schwierig. . – Seite 193/726 Funktion Zweierturm: Z(0) := 1, Z(i) := 2Z(i−1) . Z(1) = 2, Z(2) = 4, Z(3) = 16, Z(4) = 65536, Z(5) = 265536 . (Für jede Zahl z mit realem Bezug zu Anzahlen von Objekten, Zeitschritten, ... gilt z ≤ Z(5).) . – Seite 194/726 2.9 Datenstrukturen für boolesche Funktionen Darstellung von f : {0, 1}n → {0, 1}m , anders ausgedrückt Darstellung von f1 , . . . , fm : {0, 1}n → {0, 1}. f : {0, 1}n → {0, 1} Funktion log-star: log∗ n = min{k|Z(k) ≥ n}. z. B. log∗ 2000 = 4, log∗ 105 = 5, log∗ 10100 = 5. ←→ f −1 (1) Also doch nur Mengen? Ja, aber: Die Mengen sind typischerweise riesig groß. Also: limn→∞ log∗ n = ∞, aber log∗ n wächst ganz, ganz langsam. Kombinatorische Überlegung: Es gibt 2n Elemente in n {0, 1}n , also 22 verschiedene Mengen f −1 (1). Satz Zeit für m FIND- und n − 1 UNION-Operationen O((n + m) log∗ n). → Jede Datenstruktur braucht für die meisten Funktionen Ω(2n ) Bits. . – Seite 195/726 . – Seite 196/726 Ziel: Darstellung vieler und vor allem vieler wichtiger Funktionen in polynomieller Größe und Unterstützung vieler nützlicher Operationen. Welche Operationen? Bezeichne Datenstruktur für f mit Gf , da wir Graphen betrachten werden: 1.) Auswertung (evaluation) Gegeben: Gf und a. Gesucht: f (a). Motivation: offensichtlich. 2.) Gleichheitstest (equality test) Gegeben: Gf und Gg . Frage: Gilt für alle a: f (a) = g(a)? Neue Realisierung R, die f darstellen soll, sei g die dargestellte Funktion Spezifikation oder bekanntermaßen korrekte Darstellung S von f ? ∀a : S(a) = R(a) auf Schaltkreisebene zu schwierig. Datenstruktur Gf Umformung muss (oft) effizient sein, darf nie die Funktion verändern. Datenstruktur Gg Gleichheitstest Motivation: Hardwareverifikation. . – Seite 197/726 Also brauchen wir Operationen, die eine durch einen Schaltkreis dargestellte Funktion in eine Darstellung innerhalb der Datenstruktur überführen. . – Seite 198/726 3.) Synthese (binary synthesis) N N Gegeben: Gf , Gg und . Gesucht: Gh für h = f g. Achtung: Selbst kleine Größenzuwächse können sich aufschaukeln! 4.) Erfüllbarkeitstest (satisfiability test) Gegeben: Gf . Frage: ∃a : f (a) = 1? Dies ist oft ein Basisproblem (→ Vorlesung GTI). Gleichheitstest „=“ Synthese + Erfüllbarkeit: L f =g⇔f g nicht erfüllbar. Gate-by-gate transformation Datenstruktur für Literale (xi oder xi ) sollte klein und direkt herstellbar sein. Durchlaufe gate list (Gatterliste, Bausteinliste) des Schaltkreises, z.B. f g X h h=f N g mit N ∈ {AN D, OR, EXOR, . . . } EXOR . – Seite 199/726 . – Seite 200/726 5.) Ersetzung durch Konstanten ist der Spezialfall g ≡ c. Oft einfacher durchführbar. Ersetzung durch Funktionen (replacement by functions) Gegeben: Gf , Gg , xi . Gesucht: Gh für h := f|xi =g , genauer Shannon-Zerlegung: f = (xi ∧ f|xi =1 ) + (xi ∧ f|xi =0 ). h(a1 , . . . , an ) := f (a1 , . . . , ai−1 , g(a1 , . . . , an ), ai+1 , . . . , an ). Also f|xi =g = (g ∧ f|xi =1 ) + (g ∧ f|xi =0 ) = if g thenf|xi =1 else f|xi =0 = ITE(g, f|xi =1 , f|xi =0 ). Motivation: Modularer Hardwareaufbau. g Dabei benutzt f den Eingang g als formale boolesche Variable. Also: Replacement by functions = Replacement by constants + 3 binäre Synthesen = Replacement by constants + ternäre ITE-Synthese. h (Viele OBDD-Pakete unterstützen ITE-Synthese direkt.) . – Seite 201/726 6.) Abstraktion oder Quantifizierung (abstraction/ quantification) Gegeben: Gf , xi , ∀ oder ∃. Gesucht: Gg für g := (∃xi )f oder Gh für h := (∀xi )f , wobei (∃xi )f := f|xi =0 + f|xi =1 und (∀xi )f := f|xi =0 ∧ f|xi =1 . . – Seite 202/726 7.) Minimierung (minimization) Gegeben: Gf . Gesucht: Darstellung von f von minimaler Größe innerhalb der betrachteten Datenstruktur. (Wenn Ergebnis eindeutig, auch Reduktion (reduction) genannt.) Motivation: Erfüllbarkeit ⇔ (∃x1 ) . . . (∃xn )f = 1. Lebendigkeits- und Fairnessbedingungen sind Existenzaussagen. Verbotene Konstellationen lassen sich durch Allaussagen beschreiben. . – Seite 203/726 Motivation: Synthese wird einfacher, wenn irgendeine Darstellung von h das Ziel ist, aber dann entstehen oft sehr große Darstellungen. Gate-by-gate transformation nur bei integrierter Minimierung sinnvoll. . – Seite 204/726 Warum nicht gleich Schaltkreise als Datenstrukturen? π - OBDDs (ordered binary decision diagrams) Gegeben Schaltkreise Sf und Sg mit |Sf | und |Sg | Bausteinen. Die Variablenordnung π gibt die Reihenfolge an, in der die Variablen behandelt werden müssen. Auswertung: O(|Sf |). Sie ist frei wählbar, hier stets π = (x1 , . . . , xn ), wenn nichts anderes gesagt wird. Synthese: O(|Sf | + |Sg |), aber Ergebnis mit |Sf | + |Sg | + 1 Bausteinen. Der Synthesealgorithmus arbeitet nur, wenn beide OBDDs dieselbe Variablenordnung benutzen. Replacement by functions: O(|Sf | + |Sg |). Beispiel Multiplexer (Vorlesung Rechnerstrukturen): Quantifizierung: O(|Sf |). Aber: Gleichheitstest, Erfüllbarkeitstest und Minimierung sind algorithmisch schwierige Probleme (→ Vorlesung GTI). In Theorie und Anwendungen Tagungen über SAT. gute Variablenordnung: Größe Θ(n), schlechte Variablenordnung: Größe Θ(2n ). . – Seite 205/726 . – Seite 206/726 Syntax von π - OBDDs 0 - Kante x1 - Ebene x1 x2 - Ebene x1 x2 xi - Ebene x2 Jeder Knoten repräsentiert eine Funktion fv . Option 1: Auswertungsalgorithmus für fv (a). Starte an v , an xi - Knoten wähle ai - Kante, gib Wert der erreichten Senke aus. O(n) unabhängig von der OBDD - Größe. xi xi+1 - Ebene xi+1 xi+1 xi+1 .. . .. . xj - Ebene Semantik von π - OBDDs Alle Kanten sind abwärts gerichtet .. . .. . 1 - Kante xj Option 2: Globale Sicht auf alle Berechnungswege. Eingabe a aktiviert alle ai - Kanten aus xi - Knoten. Dann ist fv (a) der Wert der Senke, die auf dem eindeutigen an v startenden aktivierten Weg erreicht wird. xj .. . xn - Ebene Senken xn xn 0 1 . – Seite 207/726 . – Seite 208/726 Option 3: Bottom-up Definition Die 0 - Senke stellt die konstante Funktion 0 dar. Die 1 - Senke stellt die konstante Funktion 1 dar. Falls alle Funktionen fv für v unterhalb der xi - Ebene definiert sind und w auf xi - Ebene liegt, dann ist fw (x) := ITE(xi , fw1 (x), fw0 (x)), wobei w0 der 0 - Nachfolger und w1 der 1 - Nachfolger von w ist. s4 s3 x3 x3 y3 y3 y3 x2 y2 x2 y2 y2 x1 y1 x2 y2 y2 y2 x0 y1 y1 y1 s1 x1 x1 y1 y1 s0 x0 x0 y0 0 s2 y3 y0 1 . – Seite 209/726 . – Seite 210/726 Synthese von Gf und Gg zu Gh Erfüllbarkeitstest für fv durch „Synthese“ der Berechnungswege in Gf und Gg , hier a = (1, 0, 1, 1, 0). – Starte eine Tiefensuche von v aus, überprüfe, ob die 1-Senke erreichbar ist. O(|Gf |) fv (a) = 1 ⇒ Berechnungsweg für a erreicht die 1-Senke. fv (a) = 0 für alle a ⇒ alle Berechnungswege erreichen die 0-Senke und nicht die 1-Senke und alle Wege v → 1-Senken sind Berechnungswege. Da Kanten von oben nach unten verlaufen, enthalten Wege maximal eine Kante, die die xi -Ebene verlassen, es gibt keine widersprüchlichen Zuweisungen. Gf Gg v1 x 1 w1 x 1 Gh für h := f ⊗ g (v1 , w1 ) x1 x1 in Gf und Gg bearbeitet v2 x 2 w2 x 2 (v2 , w2 ) x2 x2 bearbeitet, aber in Gf Knoten mit „späterer Variablen“ w3 x 3 (v3 , w3 ) x3 x3 in Gg bearbeitet, in Gf „gewartet“ v3 x 4 w4 x 5 (v3 , w4 ) x4 (v4 , w4 ) x5 x4 in Gf bearbeitet, in Gg „gewartet“ x5 in Gg bearbeitet, in Gf „gewartet“ (Read-once Eigenschaft macht Erfüllbarkeit effizient.) v4 1 . – Seite 211/726 w5 0 (v4 , w5 ) 1 ⊗ 0 Senken in Gf und Gg erreicht, Ergebnis durch ⊗ verknüpft . – Seite 212/726 Synthese durch Kreuzprodukt Gf = (Vf , Ef ), Gg = (Vg , Eg ), ⊗. Gh = (Vh , Eh ) mit Vh = Vf × Vg . (Größen multiplizieren sich!) f(v,w) = [xi ∧ (fv1 ⊗ gw1 )] + [x̄i ∧ (fv0 ⊗ gw0 )] = [(xi ∧ fv1 ) ⊗ (xi ∧ gw1 )] + [(x̄i ∧ fv0 ) ⊗ (x̄i ∧ gw0 )] = (xi ∧ fv1 + x̄i ∧ fv0 ) ⊗ (xi ∧ gw1 + x̄i ∧ gw0 ) = fv ⊗ g w . Am Knoten (v, w) ∈ Vh soll fv ⊗ gw dargestellt werden. 1. Fall: v und w sind xi -Knoten w xi v xi v0 v1 w0 Gleichungen 1 und 4 sind Definitionen. Gleichungen 2 und 3 lassen sich durch Fallunterscheidung (xi = 1 und xi = 0) überprüfen. (v, w) xi w1 (v0 , w0 ) (v1 , w1 ) Falls (v0 , w0 ) fv0 ⊗ gw0 und (v1 , w1 ) fv1 ⊗ gw1 darstellt, dann gilt: . – Seite 213/726 2. Fall: v ist xi - Knoten und w ist xj - Knoten mit j > i oder Senke v xi (v, w) xi w xj v1 w0 w1 w c 3. Fall: w ist xi - Knoten und v ist xj - Knoten mit j > i oder Senke Spiegelbildlich analog zum Fall 2. 4. Fall: v ist c - Senke und w ist c0 - Senke Warten in Gg v c oder v0 . – Seite 214/726 w c0 (v, w) c ⊗ c0 (v0 , w) (v1 , w) Falls (v0 , w) fv0 ⊗ gw und (v1 , w) fv1 ⊗ gw darstellt, dann gilt f(v,w) = [xi ∧ (fv1 ⊗ gw )] + [x̄i ∧ (fv0 ⊗ gw )] = [(xi ∧ fv1 ) ⊗ (xi ∧ gw )] + [(x̄i ∧ fv0 ) ⊗ (x̄i ∧ gw )] = (xi ∧ fv1 + x̄i ∧ fv0 ) ⊗ (xi ∧ gw + x̄i ∧ gw ) = fv ⊗ g w . . – Seite 215/726 Offensichtlich ist f(v,w) = fv ⊗ g w . Also gilt Korrektheit auf Senkenebene und damit nach Induktion über die Ebenen von unten nach oben überall. O(|Gf | · |Gg |) Problem: Größenzuwachs! Größe ist Knotenzahl, da |Ef | < 2 · |Vf |. . – Seite 216/726 Wir brauchen eine dynamische Datenstruktur (computed-table), die Suchen und Einfügen unterstützt. (→ Kap.3) Synthese unter Vermeidung nicht erreichbarer Knoten Es soll ja „nur“ h am Knoten (v, w) dargestellt werden. Wenn kein Berechnungsweg, der in (v, w) startet, (v 0 , w0 ) erreicht, kann (v 0 , w0 ) gestrichen werden. – Bei Größe s gibt es Datenstruktur mit worst case Zeit O(log s) für jede Operation. – Es gibt Techniken, die bei typischen Daten Zeit O(1) pro Operation brauchen. Wir rechnen hier mit O(1) - sonst Extrafaktor O(log |Gf | + log |Gg |). Besser: (v 0 , w0 ) gar nicht erzeugen. Konstruiere Gh startend mit (v, w) (v stellt f und w stellt g dar) mit DFS-Ansatz. Wie wird erkannt, dass (v 0 , w0 ) zum zweiten Mal konstruiert wird und wir einen Zeiger auf den vorhandenen Knoten (v 0 , w0 ) konstruieren sollten? Soll (v 0 , w0 ) als neuer Knoten generiert werden, dann – Suche in Datenstruktur. – Falls gefunden, Zeiger auf vorhandenen Knoten. – Falls nicht gefunden, neuer Knoten, Zeiger auf neuen Knoten, neuen Knoten in Datenstruktur einfügen. . – Seite 217/726 . – Seite 218/726 Lässt sich das Ergebnis-π -OBDD verkleinern? Eliminationsregel: Der xi -Test kann offensichtlich übersprungen werden. Uns fallen zwei Verkleinerungsregeln ein: xi 0 v v 1 w xi xi 1 0 0 w Eliminationsregel (elimination rule) u xi w 1 u0 Formal fv = xi ∧ fw + xi ∧ fw = fw . 0 u Verschmelzungsregel: Wenn fv = fw , können die Zeiger auf v „umgebogen“ werden, um auf w zu zeigen. w 1 Es ist fv = xi ∧ fu0 + xi ∧ fu = fw . u0 Verschmelzungsregel (merging rule) . – Seite 219/726 . – Seite 220/726 Synthese unter Vermeidung nicht erreichbarer Knoten und Integration von Eliminationsregel und Verschmelzungsregel. DFS-basierte Konstruktion vom Startknoten aus. Verwendung von computed-table, um Knoten nicht mehrfach zu erzeugen. Wie sehen Zwischenergebnisse aus? Es gibt DFS hat den Backtrack auf die – bestätigte Knoten: v erste Kante zu v vollzogen. – noch nicht angelegte Knoten und – Fertige Knoten bleiben erhalten. – Wegen DFS-Konstruktion gibt es pro Ebene maximal einen Knoten in Bearbeitung. (→ nie mehr als n Kanten mehr erzeugt als im Ergebnis.) – Backtrack erreicht v über die zweite ausgehende Kante. – Integration der Eliminationsregel: Zeigen beide ausgehenden Kanten auf denselben Knoten? Falls ja, Eliminationsregel anwenden. – Knoten in Bearbeitung. . – Seite 221/726 . – Seite 222/726 Der einzige Zeiger auf v wird durch Backtrack erreicht, er – Integration der Verschmelzungsregel erhält w als Endpunkt. Der Knoten v wird in computed-table Weitere Datenstruktur, die Suchen und Einfügen unterstützt (unique-table). Dort werden Knoten durch (Variable, 1-Nachfolger, 0-Nachfolger) oder (Senke, Wert) abgespeichert. Vor dem Backtrack über die Kante zu v wird überprüft, ob ein äquivalenter Knoten (gleiches Tupel) in der unique-table ist. Ggf. Zeiger an v auf den schon vorhandenen Knoten lenken und den Repräsentanten in der computed-table speichern. Sonst Knoten in unique-table einfügen. gesucht und gefunden, dort wird die Information abgelegt, dass dieser Knoten nun durch w repräsentiert wird. Spätere Zeiger, die auf v zeigen wollen, können dann direkt auf w zeigen. . – Seite 223/726 . – Seite 224/726 Wie groß werden die Tabellen? Wie gut ist unser Ergebnis? unique-table: ein Eintrag pro Knoten im Ergebnis. computed-table: ein Eintrag pro erreichbarem Knoten im Kreuzprodukt. Satz: Ein π -OBDD ohne nicht erreichbare Knoten, auf das weder Eliminationsregel noch Verschmelzungregel anwendbar sind, hat minimale Größe. Alle minimalen π -OBDDs für f sind bis auf Benennung der Knoten identisch. Das kann viel zu viel sein! Pragmatische Lösung: Verkürze ggf. computed-table (→ Kap. 3). (In DAP2 ohne Beweis.) Was passiert mit dem Ergebnis? Schon vorhandene Knoten, die nicht gespeichert sind, werden wiederholt erzeugt, aber am Ende verschmolzen. Time-Space Trade-Off. . – Seite 225/726 Schaltkreise O(|Sf |) expo. O(1) expo. Ersetzung durch Konstanten u xi . – Seite 226/726 xi = 0 v w v Dadurch können Knoten unerreichbar und Verkleinerungsregeln anwendbar werden → in DFS-Ansatz zum Finden der xi -Knoten integrierbar. Gleichheitstest, Ersetzung durch Funktionen und Quantifizierung auf die hier behandelten Operationen zurückführbar. . – Seite 227/726 Auswertung Gleichheitstest Synthese Erfüllbarkeitstest Ersetzung durch Konstanten O(|Sf |) Ersetzung durch Funktionen O(|Sf | + |Sg |) Quantifizierung O(|Sf | + |Sg |) Minimierung expo. π -OBDDs O(n) O(|Gf | · |Gg |) O(|Gf | · |Gg |) O(|Gf |) O(|Gf |) O(|Gf |2 · |Gg |) O(|Gf | · |Gg |) O(Gf ) in Synthese integrierbar . – Seite 228/726 3 Dynamische Datenstrukturen 3.1 Vorbemerkungen Exakt: Schlüssel, unter denen Datensätze gespeichert sind. Wesentlich: Anzahl der Daten unbekannt. Beliebige Erweiterbarkeit muss möglich sein. Menge möglicher Schlüssel U (Universum, Grundmenge), meistens „Zahlen“ oder „Wörter“. Beispiele: Lineare Listen. OBDDs – üblicherweise Variablenmenge statisch, aber Funktionenmenge dynamisch. Beliebig viele Einfügungen, nur wenn |U | = ∞. Praktisch: |U | < ∞, aber für den praktischen Gebrauch groß genug, z.B. U = {0, 1}256 . Die Datensätze interessieren uns bei der Entwicklung der Datenstruktur nicht, also synonym Schlüssel, Daten, Datensätze. . – Seite 229/726 . – Seite 230/726 Wünschenswerte Operationen (nur möglich, falls Ordnung auf U gegeben ist) Notwendige Operationen: – MAKEDICT (dictionary, Wörterbuch): Erzeugt leere Datei. (Für alle betrachteten Datenstrukturen trivial.) – MIN und MAX: Suche den Datensatz der unter dem kleinsten bzw. größten Schlüssel abgespeichert ist. – SEARCH(k ): Suche nach Datensatz unter Schlüssel k und gib ihn im Erfolgsfall aus. – LIST: Liste die Datensätze geordnet nach Schlüsseln auf. – INSERT(k, x): Füge Datensatz x unter Schlüssel k in die Datei ein. Falls es bereits Datensatz unter k gibt: Fehlermeldung oder überschreiben. – CONCATENATE (D1 , D2 , D): Vereinige die Dateien D1 und D2 zu D. Nur definiert, falls maximaler Schlüssel für D1 < minimaler Schlüssel für D2 . – DELETE(k ): Entferne den Datensatz, der unter Schlüssel k gespeichert ist. Falls es keinen solchen gibt: Fehlermeldung. – SPLIT(D, k, D1 , D2 ): Zerlege die Datei D in D1 (alle Datensätze mit Schlüsseln k 0 ≤ k ) und D2 (Rest). . – Seite 231/726 . – Seite 232/726 Übersicht 3.2 Hashing Zur Erinnerung: 3.2 Hashing – schlechter worst case: O(n) bei n Daten, guter average case bei zufälligen Daten: O(1). – Unsortierte Arrays unterstützen SEARCH nicht effizient. 3.3 Binäre Suchbäume – ähnlich, aber schlechterer average case: O(log n). – Sortierte Arrays unterstützen INSERT nicht effizient. 3.4 2-3-Bäume – worst case O(log n). Dennoch basiert geschlossenes Hashing auf Arrays. 3.5 B-Bäume – Erweiterung für Paging-Konzept. Das Array A habe die Positionen 0, . . . , M − 1. 3.6 AVL-Bäume – worst case O(log n), aber bessere Speicherplatznutzung als 2-3-Bäume. Bei Overflow (oder schon bei zu großer Auslastung, evtl. mehr als 95%) ist Rehashing in ein neues Array notwendig! 3.7 Skiplisten – eine randomisierte Datenstruktur mit erwarteter Zeit O(log n), einfach implementierbar, praktisch schnell. . – Seite 233/726 . – Seite 234/726 Voraussetzung: U = {0, . . . , N − 1} mit N >> M . Hashfunktion h : U → {0, . . . , M − 1}. Achtung: Daten sind oft nicht gleichverteilt. h ist gut geeignet, wenn gilt: – h ist effizient zu berechnen, Z.B.: Texte in Zahlen übertragen, oft viele Leerzeichen, . . . – h streut gut, d.h.: ∀i, j ∈ {0, . . . , M − 1} : |h−1 (i)| ≈ |h−1 (j)|. M Zweierpotenz: x mod M wählt nur die letzten log M Bits. M Primzahl: x mod M „beeinflusst“ alle Bits. Meistens benutzt: h(x) :≡ x mod M . Dann ist |h−1 (i)| Auswertung von h gilt als eine Operation. ∈ {bN/M c, dN/M e} . . – Seite 235/726 . – Seite 236/726 M = 365 N >> M ⇒ ∃i : |h−1 (i)| ≥ N/M. Also gibt es Kollisionen von Daten. Sind diese auch wahrscheinlich bei n Daten und n << M ? Geburtstagsparadox Annahme: Daten unabhängig und Prob(h(x) = j ) = 1/M für alle x. Prob(i-tes Datum kollidiert nicht mit den ersten i − 1 Daten, wenn diese kollisionsfrei sind) = M −(i−1) . M Prob(n Daten kollisionsfrei) = M −1 M · M −2 M · · · M −n+1 . M Prob(23 Daten kollisionsfrei) ≈ 0,49, Prob(50 Daten kollisionsfrei) ≈ 0,03. Prob(2M 1/2 Daten kollisionsfrei) = M −1 M − M 1/2 M − 2M 1/2 + 1 ··· ··· M M M | {z } | {z } !M 1/2 M 1/2 M − M 1/2 1 1 ≤ 1 · = 1 − 1/2 ≈ M e M −→ Hashing muss mit Kollisionen leben und benötigt Strategien zur Kollisionsbehandlung. . – Seite 237/726 t(i, j) := Position des i-ten Versuchs zum Einfügen von Daten x mit h(x) = j . . – Seite 238/726 INSERT(x) nach erfolgloser Suche: Es wurde freier Platz gefunden (sonst Overflow) und füge x dort ein. Auch t soll in Zeit O(1) berechenbar sein, t(0, j) = j und t(·, j) : {0, . . . , M − 1} → {0, . . . , M − 1} soll bijektiv sein. DELETE(x) nach erfolgreicher Suche: Das Datum kann nicht einfach entfernt werden, da dann SEARCH(x) frühzeitig Lücken findet und eine Suche fälschlicherweise als erfolglos abbrechen kann. SEARCH(x): – Berechne j := h(x). – Suche x an den Positionen t(0, j), . . . , t(M − 1, j). – Abbruch, wenn x gefunden oder freie Stelle entdeckt. Dann gibt es kein Datum unter Schlüssel x. . – Seite 239/726 . – Seite 240/726 Ausweg: Markiere Positionen als „besetzt“ „noch nie besetzt“ und „wieder frei“. Strategien zur Kollisionsbehandlung (alle Rechnungen mod M ) Eine Suche wird nur an Positionen „noch nie besetzt“ vorzeitig abgebrochen. Im Laufe der Zeit gibt es keine Positionen „noch nie besetzt“ mehr. Lineares Sondieren (linear probing) t(i, j) := i + j . Dann ist Hashing ineffizient. Reihenfolge 7,8,9,10,11,12,13,14,15,16,17,18,0,1,2,3,4,5,6. Beispiel für alle Stategien: M = 19 und j = h(x) = 7. → Geschlossenes Hashing nur bei SEARCH und INSERT. Z.B. unique-table und computed-table bei OBDD-Synthese. . – Seite 241/726 . – Seite 242/726 Problem der Klumpenbildung Ziel Egal, welche Positionen belegt sind, haben die freien Positionen dieselbe W.keit besetzt zu werden. Die Positionen 2, 5, 6, 9, 10, 11, 12, 17 seien belegt. h(x): 0 1 2 3 4 5 6 7 8 9 10 landet an Position: 0 1 3 3 4 7 7 7 8 13 13 1 1 1 1 2 3 W.keit 0 19 0 0 19 0 0 19 19 19 19 h(x): 11 landet an Position: 13 W.keit 0 Das geht numerisch nicht genau. Im Beispiel: 11 freie Plätze, 19 Hashwerte, also alle W.keiten k/19 und nicht 1/11. Modell des idealen Hashing Alle M n Möglichkeiten, die n besetzten Plätze bei n Daten 12 13 14 15 16 17 18 13 13 14 15 16 18 18 1 1 1 5 2 0 0 19 19 19 19 19 auszuwählen, haben dieselbe Wahrscheinlichkeit. Datenklumpen erhöhen Suchzeiten. . – Seite 243/726 . – Seite 244/726 Lineares Sondieren ist weit vom idealen Hashing entfernt. M = 19, j = h(x) = 7 Quadratisches Sondieren (quadratic probing) Reihenfolge: j = h(x) 7 , 8, 6, Probierreihenfolge: j , j + 12 , j − 12 , j + 22 , j − 22 ,. . ., j + ( M2−1 )2 , j − ( M2−1 )2 . 11, 3, 16, −2 = 23 = −9 = 32 = −18 = 17, 4, 10, 13, 1, 43 = −29 = 56 = −42 = 71 = −57 = 88 = −74 = 9, 18, 15, 14, 0, 12, 2. 5, 2 Als Formel: t(i, j) = j + (−1)i+1 · b i+1 2 c . . – Seite 245/726 . – Seite 246/726 Ist t(·, j) für alle j und M bijektiv? Multiplikatives Sondieren (add to hash) Nein, aber immer wenn M ≡ 3 mod 4 und Primzahl ist. Beweis: Zahlentheorie (Stichwort quadratische Reste). Es sei h(x) = x mod (M − 1) + 1 und damit in {1, . . . , M − 1}. Besser als lineares Sondieren, aber für großes M sind die ersten Werte noch „nah“ an j . Arraypositionen 1, . . . , M − 1. t(i, j) = i · j , 1 ≤ i ≤ M − 1. M = 19, j = h(x) = 7. . – Seite 247/726 i·j i · j mod 19 7 14 7 14 i·j i · j mod 19 70 13 21 2 28 9 77 84 91 1 8 15 35 16 98 3 42 4 105 10 49 11 56 63 18 6 112 17 119 126 5 12 . – Seite 248/726 Ist t(·, j) für alle j und M bijektiv? Doppeltes Hashing (double hashing) Nein,aber ja für Primzahlen M und j 6= 0. Es seien h1 (x) ≡ x mod M , h2 (x) ≡ x mod (M − 2) + 1. i-te Position für x: h1 (x) + i · h2 (x) mod M . Beweis: durch Widerspruch. Falls nicht, gibt es 1 ≤ i1 < i2 ≤ M − 1 mit Es sei M = 19 und x = 47. Dann ist h1 (x) = 9 und h2 (x) = 14. i1 · j ≡ i2 · j mod M ⇒ j · (i2 − i1 ) ≡ 0 mod M ⇒ j · (i2 − i1 ) ist Vielfaches von M ⇒ Primfaktorzerlegung von j · (i2 − i1 ) muss M enhalten. Die Probierreihenfolge: 9, 4, 18, 13, 8, 3, 17, 12, 7, 2, 16, 11, 6, 1, 15, 10, 5, 0, 14. Widerspruch, da 1 ≤ j ≤ M − 1, 1 ≤ i2 − i1 ≤ M − 1. . – Seite 249/726 Wie beim multiplikativen Hashing durchläuft i · h2 (x), 1 ≤ i ≤ M − 1, die Werte 1, . . . , M − 1 in irgendeiner Reihenfolge, wir fügen 0 = 0 · h2 (x) hinzu. . – Seite 250/726 Analyse der 4 Kollisionsstrategien ist schwierig. Doppeltes Hashing kommt dem idealen Hashing am nächs- Lineares Sondieren: 95% Auslastung bei großen Arrays. Erfolgreiche Suche: durchschnittlich ungefähr 10, 5 Positionen. Erfolglose Suche: durchschnittlich ungefähr 200, 5 Positionen. ten. Wir analysieren zum Vergleich ideales Hashing. Durch den Summanden h1 (x) wird der Anfang zufällig verschoben. . – Seite 251/726 . – Seite 252/726 Also Erfolglose Suche beim idealen Hashing – Datei nicht voll, d. h. M ≥ n + 1, n Daten abgespeichert. – f (n, M ) := erwartete Suchzeit. f (n, M ) = 1 + = 1+ – mit W.keit 1 Suche an Position t(0, h(x)). −n W.keit MM : Position leer, Restkosten 0. = 1+ n : Position besetzt. W.keit M = 1+ Erwartete Restkosten f (n − 1, M − 1) (Restarray der Länge M − 1 enthält n − 1 zufällig verteilte Daten.) = 1+ M −n n ·0+ · f (n − 1, M − 1) M M n · f (n − 1, M − 1) M n−1 n · 1+ · f (n − 2, M − 2) M M −1 n n n−1 n−2 + · · 1+ · f (n − 3, M − 3) M M M −1 M −2 n n n−1 n n−1 n−2 + · + · · + ... M M M −1 m M −1 M −2 Und jetzt? Ausprobieren, raten, ... . – Seite 253/726 f (n, M ) = . – Seite 254/726 M +1 M +1−n . Beweis durch Induktion über n. n = 0 : f (0, M ) = 1 und M +1 M +1−0 n−1→n: f (n, M ) = Ind.vss. = = Erfolgreiche Suche beim idealen Hashing = 1. Gesuchtes Datum x wurde als k -tes Datum eingefügt. Erfolgreiche Suche nach x verläuft bis zum Finden von x identisch zu der erfolglosen Suche nach x direkt vor dem Einfügen von x. M +1 Dann k − 1 besetzte Stellen, erwartete Suchzeit M +1−(k−1) n · f (n − 1, M − 1) M n M 1+ · M M − (n − 1) M +1 n = . 1+ M +1−n M +1−n 1+ Wenn nun jedes der n Daten mit W.keit 1 n gesucht wird, be- trägt die erwartete Suchzeit 95% Auslastung, d. h. n ≈ 0, 95 · (M + 1), dann ≈ 20 Positionen im Schnitt. (Ersparnisfaktor 10 gegenüber linearem Sondieren.) . – Seite 255/726 . – Seite 256/726 1 n P 1≤k≤n M +1 n M +1 M −k+2 ln(m + 1) ≤ H(m) ≤ ln m + 1, also H(m) ≈ ln m. = 1 1 1 + ··· + M −n+2 M +1 {z P } | P 1≤i≤M +1 1 2 1 i − 1≤i≤M −n+1 1 x f (x) = H(m) ≤ 1 + 1 2 1 3 1 4 1 i 1 1 1 m 1 + + · · · + kommt oft vor, heißt harmonische Reihe, ihr Wert wird mit H(m) bezeichnet. Rm 1 2 f (x) = 3 4 x dx = ln m + 1 m 1 x 1 Aber wie groß ist H(m) ungefähr? H(m) ≥ m+1 R 1 1 2 1 3 1 4 1 2 3 4 1 dx x = ln(m + 1) m+1 . – Seite 257/726 Die erwartete Zeit einer erfolgreichen Suche beim idealen Hashing beträgt M +1 n (H(M + 1) − H(M − n + 1)) ≈ Mn+1 (ln(M + 1) − ln(M − n + 1)) Für n ≈ 0, 95 · (M + 1) ist dies 1 0,95 = M +1 n +1 ln MM+1−n . · ln 1 0,05 ≈ 3, 15. . – Seite 258/726 Eine Variante des geschlossenen Hashing An jeder Arrayposition Platz für c Daten. Kollisionsbehandlung erst für c + 1 Daten an einem Platz. Ersparnisfaktor größer als 3 gegenüber linearem Sondieren. . – Seite 259/726 Computed-table bei OBDD-Synthese: c = 4 und keine Kollisionsbehandlung, sondern Überschreiben der am längsten nicht gelesenen Information. . – Seite 260/726 Offenes Hashing Vorteil: auch DELETE wird unterstützt, kein Overflow. Nachteil: mehr Speicherplatz durch Benutzung von Listen. Datum steht stets in der Liste L(h(x)). SEARCH(x): Berechne h(x) und suche in der Liste L(h(x)). INSERT(x) nach erfolgloser Suche: Füge x in L(h(x)) ein, z. B. stets vorne. O(1) DELETE(x) nach erfolgreicher Suche: Entferne x, nur effizient, wenn Zeiger auf x bei der Suche gespeichert wird. O(1) Bei zufälligen Daten und ideal streuenden Hashfunktion gilt für ( 1 i-tes Datum kommt in j -te Liste Xij := 0 sonst Prob(Xij = 1) = 1 M. Also E(Xij ) = 1 · 1 M +0· M −1 M = 1 M. Xj = X1j + · · · + Xnj zählt Daten in j -ter Liste. E(Xj ) = E(X1j + · · · + Xnj ) = E(X1j ) + · · · + E(Xnj ) = . – Seite 261/726 Erfolglose Suche in Liste j Inklusive nil-Zeiger durchschnittlich 1 + betrachten. Falls n ≈ 0, 95 · M , ist dies ≈ 1, 95. n M n M. . – Seite 262/726 Offenes Hashing erlaubt n > M . Falls n M , Rehashing wegen fallender Effizienz ratsam. Objekte Erfolgreiche Suche in Liste j der Länge l Jede Position in der Liste hat W.keit 1/l, also 1 l+1 l (1 + 2 + · · · + l) = 2 . Durchschnittliche Listenlänge hier: 1 + n−1 M , Liste enthält sicher das gesuchte Datum und die anderen n − 1 Daten sind zufällig verteilt. n−1 Also erwartete Suchdauer 12 (1 + n−1 M + 1) = 1 + 2M . Alle Ergebnisse für zufällige Daten. Ausgefeiltere Methoden: Kompakt beschreibbare Hashfunktionen (viele) und Auswahl einer zufälligen Funktion dieser Klasse. Gutes erwartetes Verhalten bei allen Daten. (→ Spezialvorlesung) Falls n ≈ 0, 95 · M , ist dies ≈ 1, 475. . – Seite 263/726 . – Seite 264/726 3.3 Binäre Suchbäume Espe In Kap. 3.3 - 3.7 U vollständig geordnet. Erle Binärer Suchbaum: Kiefer – gewurzelter Baum mit Zeigern in Richtung Blätter, Birke nil Fichte Tanne – innere Knoten dürfen nil-Zeiger und Zeiger auf Knoten haben, Ahorn – Knoten enthalten ein Datum, – Suchbaumeigenschaft: Knoten mit Datum x ⇒ alle Daten im linken Teilbaum sind kleiner als x und alle Daten im rechten Teilbaum sind größer als x. nil nil Eiche nil Buche nil nil Linde nil nil nil Ulme nil nil nil . – Seite 265/726 . – Seite 266/726 SEARCH(x): – Starte an der Wurzel. DELETE etwas später. – Es wird Knoten mit Datum y erreicht. – y = x, Suche erfolgreich. – y > x, gehe zum linken Kind, – y < x, gehe zum rechten Kind. Die Suchzeit hängt von der Länge des Weges Wurzel → Knoten mit Datum x (oder nil-Zeiger, wo x hingehört) ab. Entstehen typischerweise gut balancierte Bäume? – Es wird ein nil-Zeiger erreicht. – x ist nicht im Suchbaum enthalten. Beispiel: Füge eins, zwei, ..., zehn in einen leeren binären Suchbaum ein. Im Bild keine nil-Zeiger. INSERT(x) nach erfolgloser Suche: – Der erreichte nil-Zeiger wird ersetzt durch einen Zeiger x auf nil nil . O(1) . – Seite 267/726 . – Seite 268/726 eins zwei drei zwei drei drei zwei Bilanz: zwei Nach Einfügen von „sieben“ schon ein Weg mit 6 Daten, der dann aber nicht mehr verlängert wurde. vier vier vier zwei eins eins eins drei eins eins fünf fünf sechs DELETE(x) nach erfolgreicher Suche, die den Zeiger auf den Knoten v mit Datum x bereitstellt: eins eins drei drei zwei acht vier zwei vier 1.Fall: v ist Blatt. Zeiger wird durch nil-Zeiger ersetzt. (Knoten v wird freigegeben.) fünf fünf sechs sechs sieben sieben eins drei acht vier drei acht fünf zwei vier zehn fünf sechs neun 2.Fall: v hat genau ein Kind. Zeiger wird durch Zeiger auf Kind ersetzt. (Knoten v wird freigegeben.) eins zwei sechs sieben neun sieben . – Seite 269/726 3.Fall: v hat zwei Kinder. Suche größtes Datum y , das kleiner als x ist. (Suche nach y würde auch x erreichen, sonst wäre alles im linken Teilbaum von v zwischen y und x, Widerspruch.) Also gehe zum linken Kind und dann stets nach rechts bis zu einem nil-Zeiger. Letztes Datum ist y . Vertausche x und y (kein Suchbaum mehr!). Entferne x im neuen Knoten (das ist Fall 1 oder Fall 2). Korrektheit? Erst einmal Beispiele. . – Seite 271/726 . – Seite 270/726 DELETE (sieben) DELETE (zwei) DELETE (vier) eins eins eins zwei drei acht vier .. funf drei acht .. funf zehn sechs drei vier zehn sechs acht sechs .. funf zehn neun neun neun . – Seite 272/726 Allgemeine Überlegungen zur Korrektheit DELETE(xi ): Daten x1 < x2 < · · · < xn , n + 1 nil-Zeiger auf die Suchbereiche (−∞, x1 ), (x1 , x2 ), . . . , (xn−1 , xn ), (xn , ∞). Die Suchbereiche (xi−1 , xi ), (xi , xi+1 ) sollen zu (xi−1 , xi+1 ) verschmolzen werden. 1. Fall Dies gilt im leeren Baum (−∞, ∞) und muss bei INSERT und DELETE erhalten bleiben. xi (xi−1 , xi ) INSERT(y) mit xi < y < xi+1 (x0 := −∞, xn+1 := ∞). Suche findet nil-Zeiger auf (xi , xi+1 ), sonst wären wir im Bereich „< xi “ oder „> xi+1 “ und das vermeidet SEARCH. Dieser Bereich wird zerlegt: (xi−1 , xi+1 ) (xi , xi+1 ) 2. Fall xi (xi , xi+1 ) y (xi , xi+1 ) (xi−1 , xi ) (xi , y) (y, xi+1 ) (xi−1 , xi+1 ) . – Seite 273/726 3. Fall . – Seite 274/726 Analyse der erfolgreichen Suche, (u, xi ) (xi , z) xi−1 wenn jedes der n Daten mit W.keit 1/n gesucht wird. xi−1 xi xi+1 (u, xi−1 ) (v, xi−1 ) Worst case: „Lineare Liste“ (xi−1 , z) durchschnittliche Suchzeit xi+1 (v, xi−1 ) (xi−1 , xi )(xi , xi+1 ) 1 n (xi−1 , xi+1 ) . – Seite 275/726 · (1 + 2 + · · · + n) = n+1 2 . . – Seite 276/726 Erwartete Suchzeit = Best Case: „vollständig balancierter Baum“ Sei n = 2k − 1. 1 n 20 Daten Suchzeit 1 21 Daten Suchzeit 2 22 Daten Suchzeit 3 = · (k · 2k−1 + (k − 1) · 2k−2 + (k − 2) · 2k−3 + · · · 1 n( 2k−1 + 2k−2 + +2k−1 + 2k−2 + +2k−1 + 2k−2 + ... +2 2 k−1 Daten k−1 +1 · 20 ) 2k−3 + · · · + 2k−3 + · · · 20 = 1 n( +21 2k−3 + · · · +22 2k − 1 +2k − 2 +2k − 22 +2k − 2k−1 ) ) Suchzeit k = 1 n · (k · 2k − (2k − 1)) = 1 n · (log(n + 1) · (n + 1) − n) = (1 + n1 ) · log(n + 1) − 1 ≈ log n. . – Seite 277/726 Zwischen log n und Was ist typischer? n+1 2 . – Seite 278/726 liegen Welten! Modell: n Daten werden in zufälliger Reihenfolge in einen leeren Baum eingefügt, dann erwartete Suchzeit, Daten o. B. d. A. 1, . . . , n. Dies ist ein „doppelter“ Erwartungswert: C(n) := durchschnittliche Kosten aller Suchbäume zu allen Datenreihenfolgen. Wir wollen eine Rekursionsgleichung erstellen. Es ist C(0) = 0, C(1) = 1. P C(n) = n + n1 (C(i − 1) + C(n − i)). 1≤i≤n – Wahl einer von n! Datenreihenfolgen nach Gleichverteilung, Warum? – Jeder Suchpfad besucht die Wurzel, Summand n. – Wahl eines von n Daten für die Suche nach Gleichverteilung. – Die Wurzel enthält das erste eingefügte Datum, mit W.keit 1/n ist es das Datum i. Vereinfachung: C(T ) = Kosten eines Suchbaums T := Summe der Suchkosten für alle Daten. (Ergebnis durch n teilen.) . – Seite 279/726 . – Seite 280/726 1 n C(n) = n + – Dann Daten 1, . . . , i − 1 in den linken Teilbaum. 2 n =n+ – Diese Daten kommen in zufälliger Reihenfolge, also durchschnittliche Suchkosten C(i − 1). P 1≤i≤n P (C(i − 1) + C(n − i)) C(i). 1≤i≤n−1 Ziel: C(1), C(2), . . . , C(n − 1), C(n) → C(n − 1), C(n) . 2 n · C(n) = n + 2 · (C(1) + · · · +C(n − 2) + C(n − 1)). I − II (n − 1) · C(n − 1) = (n − 1)2 + 2 · (C(1) + · · · +C(n − 2)). n · C(n) − (n − 1) · C(n − 1) = 2n − 1 + 2C(n − 1) – Analog Daten i + 1, . . . , n in den rechten Teilbaum, durchschnittliche Suchkosten C(n − i). – Schwierige Rekursionsgleichung, aber – typische Methoden und – dieselbe Rekursionsgleichung bei Quicksort. ⇒ n · C(n) − (n + 1) · C(n − 1) = 2n − 1. passt nicht gut zusammen → Division durch n · (n + 1). . – Seite 281/726 C(n) n+1 Z(n) C(n−1) 2n−1 = n(n+1) . n := C(n) n+1 → . – Seite 282/726 − −2 2n − 1 Z(n) = Z(n − 1) + n(n + 1) X 2i − 1 = Z(0) + |{z} i(i + 1) =0 = X 1≤i≤n = X 1≤i≤n 1≤i≤n 1 1 1 = − i(i + 1) i i+1 1≤i≤n = −2 · ( P 1≤i≤n 1− P 1≤i≤n 1 i+1 ) = −2n + (2 · H(n) − 2 + 2 n+1 ) 1 − 2n + 2 · H(n) − 2 + Z(n) = 2n − 1 + n+1 3 = 2 · H(n) − 3 + n+1 . 2 n+1 n+1 n+1 n+1 3 Z(n) = 2 · · H(n) − 3 · + n n n n = 2 · ln n − O(1) = (2 ln 2) log n − O(1). | {z } C(n)/n = X 1 X X 1 i −2 + . i i+1 i+1 1≤i≤n 1≤i≤n i i+1 Also erwartete Suchzeit 1 1 (2i − 1) · ( − ) i i+1 2− P 1≤i≤n ≈1,386 2n Nur 38, 6% schlechter als im best case. −1 + 1 n+1 . – Seite 283/726 . – Seite 284/726 In vielen Anwendungen ist die Annahme einer zufälligen Reihenfolge der Daten nicht gerechtfertigt. 3.4 2–3–Bäume (Hopcroft, 1970) Definition – Pro Knoten ein Datum oder zwei Daten. Ziel: Suchbaumvarianten mit worst case Tiefe O(log n). – Ein Datum x, dann zwei Zeiger, links nur kleinere und rechts nur größere Daten als x. – Zwei Daten x und y > x, dann drei Zeiger, links nur Daten kleiner als x, in der Mitte nur Daten zwischen x und y , rechts nur Daten größer als y . – Alle Zeiger, die einen Knoten verlassen, sind nil oder nicht nil. – Alle Blätter auf einer Ebene. . – Seite 285/726 n Daten, Tiefe d ⇒ dlog3 (n + 1)e − 1 ≤ d ≤ blog2 (n + 1)c − 1. Existenz von 2-3-Bäumen mit n Daten? – Beweis durch INSERT-Prozedur. Größte Datenzahl auf d + 1 Ebenen: 2 · (1 + 3 + · · · + 3d ) = 3d+1 − 1. Speicherplatzausnutzung? Knoten Zeiger Datum . – Seite 286/726 Zeiger Datum Also n ≤ 3d+1 − 1 und d ≥ dlog3 (n + 1)e − 1. Zeiger Kleinste Datenzahl auf d + 1 Ebenen: Die letzten beiden Komponenten können leer sein. 1 + 2 + · · · + 2d = 2d+1 − 1. Worst case Tiefe? Also n ≥ 2d+1 − 1 und d ≤ blog2 (n + 1)c − 1. Einfach zu analysieren. . – Seite 287/726 . – Seite 288/726 INSERT(x) nach erfolgreicher Suche Suchbereiche (v, y) x (v, x) (x, y) (v, z) x, y Zunächst wie bei binären Suchbäumen. Problem: Es gibt einen Zeiger (v, w), der auf einen Teilbaum zeigt, dessen Wurzel ein Datum (zunächst x) enthält und dessen Blätter eine Ebene zu tief sind. (v, x) (x, y) (y, z) 1. Fall: (v, w) verlässt Knoten mit einem Datum. (z, z 0 ) y SEARCH(x) Verlauf sollte klar sein. Hierbei wird Suchpfad auf Stack abgespeichert. (z, y) (y, z 0 ) . – Seite 289/726 Falls (z, y) = (v, w): . – Seite 290/726 2. Fall: (v, w) verlässt Knoten mit zwei Daten. (z, z 0 ) y, y 0 (z, z 0 ) x, y (z, y) (y, y 0 )(y 0 , z 0 ) 0 (z, x) (x, y) (y, z ) Falls (z, y) = (v, w): Der Fall (y, z 0 ) = (v, w) verläuft analog. (z, z 0 ) Die Rebalancierung ist erfolgreich abgeschlossen. x, y, y 0 (z, x)(x, y)(y, y 0 )(y 0 , z 0 ) . – Seite 291/726 (z, z 0 ) y (z, y) (y, z 0 ) x y0 (z, x) (x, y)(y, y 0 ) (y 0 , z 0 ) . – Seite 292/726 Neues Problem am Zeiger (z, z 0 ), dieser Zeiger liegt auf dem Stack. Falls (y, y 0 ) = (v, w): (z, z 0 ) x (z, z 0 ) Wenn dies nicht der Zeiger auf die Wurzel ist, Rebalancierung dort fortsetzen. y, x, y 0 Wenn dies der Zeiger auf die Wurzel ist, Rebalancierung abgeschlossen, Gesamttiefe um 1 gewachsen. (z, y)(y, x)(x, y 0 )(y 0 , z 0 ) y y0 (z, y) (y, x)(x, y 0 ) (y 0 , z 0 ) Neues Problem an Zeiger (z, z 0 ). . – Seite 293/726 Falls (y 0 , z 0 ) = (v, w): Die Tiefe des 2-3-Baumes wächst, wenn die Wurzel erreicht und „gesplittet“ wird. (z, y)(y, y 0 )(y 0 , x)(x, z 0 ) → „2-3-Bäume wachsen über die Wurzel.“ y0 (z, z 0 ) y, y 0 , x . – Seite 294/726 y Beispiel: Einfügen der Buchstaben A, L, G, O, R, I, T, H, M, U, S in einen leeren 2-3-Baum. x (z, y) (y, y 0 )(y 0 , x) (x, z 0 ) Neues Problem an Zeiger (z, z 0 ). . – Seite 295/726 . – Seite 296/726 A A,L G,O G A,L G A A L A R,T I,L H G G I A L,O L L A I,L I O G R L H R,T R I H L,M A I,L O G R,T A G,O G,O R A O A L,O A A L G R G,O O G R,T H I A I G,O H L,M G R,T A O,T H L,M R U U R,T I G A O,T H L,M R,S U . – Seite 297/726 DELETE(x) nach erfolgreicher Suche (1) Falls x nicht in einem Blatt, suche das größte Datum y mit y < x. Ein Zeiger, der den Knoten mit x verlässt, zeigt auf einen Bereich (z, x). Falls z = y , ist dies ein nil-Zeiger und x in einem Blatt. Sonst enthält dieser Bereich y . Folge diesem Zeiger und dann stets dem rechtesten Zeiger. Vertausche x und y und entferne dann x aus einem Blatt. . – Seite 299/726 . – Seite 298/726 Es gab die Suchbereiche (z, y), (y, x) und (x, z 0 ). Die Suchbereiche (z, y) und (y, x) werden verschmolzen und x durch y ersetzt. Also entstehen wie gewünscht die Suchbereiche (z, y) und (y, z 0 ). Zu behandeln: Entfernung aus einem Blatt. (2) Wir verschmelzen die beiden Zeiger, deren Suchbereiche verschmolzen werden sollen, und entfernen das trennende Datum. → Knoten mit einem Datum und zwei Zeigern. Rebalancierung abgeschlossen. oder → Knoten ohne Datum mit einem Zeiger → (3). . – Seite 300/726 Ansonsten zwei Fälle: (3) Wir haben „2-3-Baum mit Fehler“. 1. Fall: ∃ direkter Geschwisterknoten mit 2 Daten → Datenrotation Der Fehler ist ein Knoten ohne Datum mit einem Zeiger. Suchbaumeigenschaften gelten. v ...c p u Abbruchkriterium: Fehler an der Wurzel, diese entfällt und Kind ist neue Wurzel. p q w d, e p0 q1 u ...d v c p0 q 2 q3 w e q1 q2 q3 (b, c) (c, d) (d, e) (e, f ) (b, c) (c, d) (d, e) (e, f ) Rebalancierung abgeschlossen. „2-3-Bäume schrumpfen über ihre Wurzel.“ . – Seite 301/726 2. Fall: Direkte Geschwisterknoten enthalten nur ein Datum, o. B. d. A. existiert ein rechter Geschwisterknoten. v ...c p u p d p 0 q1 (b, c) (c, d) (d, e) 8 8 DELETE (12) 4 12 4 11 ∗ w q2 Beispiel v ... q . – Seite 302/726 2 c, d p0 q1 6 10 14 2 6 10 14 w∗ 1 3 5 7 9 11 13 15 1 3 5 7 9 13 15 q2 (b, c) (c, d) (d, e) 8 Hatte Elterknoten zwei Daten, Rebalancierung abgeschlossen. Hatte Elterknoten ein Datum, Problem eine Ebene nach oben verschoben. 11 4 2 1 . – Seite 303/726 4 6 3 8 DELETE (12) 5 14 7 9,10 13 2 15 1 11,14 6 3 5 7 9,10 13 15 . – Seite 304/726 4,7 4,8 4,8 DELETE (13) 2 2 6 2 11,14 6 3 5 7 9,10 13 15 1 3 5 6 11 2 7 9 11 3 5 9,10 15 4,8 6 10,11 2 3 5,6 3 11 3 5 7 9 15 1 5,6 9,10 15 1 11 3 5,6 5 9 15 11 6 3 15 DELETE (9) 2,4 7 1 9,10 7 DELETE (10) 2,4 DELETE (8) 1 2 1 15 7 4,8 11 10,14 1 1 4,7 DELETE (14) 7 9,10 4 15 2,4 1 3 2 5,6 11,15 1 7 3 5,6 11,15 . – Seite 305/726 . – Seite 306/726 2-3-Bäume unterstützen auch MIN / MAX (O(log n)) und LIST (O(n)). Rechenzeitanalyse SEARCH(x): Ein Suchpfad. O(log n). INSERT(x) nach erfolgloser Suche: Suchpfad rückwärts O(1) pro Knoten → O(log n). DELETE(x) nach erfolgreicher Suche: eventuell anschließend Suche nach y , dann Suchpfad rückwärts, O(1) pro Knoten → O(log n). Für CONCATENATE und SPLIT folgende Variante besser geeignet: – Daten nur in den Blättern. – Knoten mit zwei Zeigern enthält größtes Datum des linken Teilbaumes. – Knoten mit drei Zeigern enthält größtes Datum des linken und des mittleren Teilbaumes. – Wurzel enthält zusätzlich Tiefe und größtes Datum. Modifikationen an SEARCH, INSERT und DELETE: selber überlegen. . – Seite 307/726 . – Seite 308/726 CONCATENATE(T1 , T2 , T ) 2. Fall: d(T1 ) > d(T2 ) (d(T1 ) < d(T2 ) ähnlich) 1. Fall: d(T1 ) = d(T2 ) T MAX(T1 ) MAX(T2 ) (d(T ) := d(T1 ) + 1, MAX(T ) := MAX(T2 )) v : von der Wurzel von T1 d(T1 ) − d(T2 ) − 1 Schritte nach rechts. T2 erhält neue Dummy-Wurzel. v T1 T2 O(1) T1 T2 . – Seite 309/726 1. Unterfall: v hat nur zwei Zeiger. v T1 T2 . – Seite 310/726 2. Unterfall: v hat drei Zeiger. v erhält die alten beiden Zeiger und Zeiger auf die Wurzel von T2 , v übernimmt die alte Information und von der Wurzel von T1 die Information M AX(T1 ). An der Wurzel ist nun MAX(T ) := MAX(T2 ). O(d(T2 )−d(T1 )). Analog zum 1. Unterfall, nur muss v „platzen“, wie bei INSERT Rebalancierung nach oben. O(d(T2 )−d(T1 )). v T1 T2 Insgesamt O(d(T2 ) − d(T1 ) + 1). . – Seite 311/726 . – Seite 312/726 T1 ist Konkatenation aller Bäume der „≤ - Gruppe“, analog T2 und „> - Gruppe“. SPLIT(T, a, T1 , T2 ) Führe SEARCH(a) durch und speichere alle Geschwisterknoten in einer „≤ - Gruppe“ und einer „> - Gruppe“. Das Blatt mit a wird geteilt, wenn es ein Datum b > a enthält (a → „≤“, b → „>“), sonst in „≤ - Gruppe“. O(d(T )) ≤ T1 ist Konkatenation von T11 , . . . , T1m mit d(T1i ) ≥ d(T1i+1 ). > ≤ > ≤ Für i = m − 1, . . . , 1: CONCATENATE(T1i , T1i+1 , T1i ). T1 := T11 . > Korrektheit offensichtlich. ≤ Rechenzeit O(d(T )). ≤ Dazu brauchen wir eine > a, b > a ≤ b > . – Seite 313/726 T1i Zwischenbehauptung: Konkatenation aller Teilbäume mit d(T1i ) ≤ d0 ergibt 2-3-Baum der Tiefe höchstens d0 + 1. d0 = 0: höchstens 6 Daten → Tiefe 1 genügt. 0 0 d → d + 1: Alle Teilbäume mit Tiefe ≤ d0 ergeben Baum T 0 der Tiefe ≤ d0 + 1. In der T1i -Folge gibt es höchstens 2 Bäume T1j und T1j+1 mit Tiefe d0 + 1. Konkateniere T1j+1 und T 0 zu T 00 mit - Tiefe d0 + 1 oder - Tiefe d0 + 2, wobei Wurzel zwei Zeiger hat. Konkateniere T1j und T 00 mit - Tiefe höchstens d0 + 2, da Wurzel nicht „platzt“. . – Seite 315/726 . – Seite 314/726 Es seien d1 < · · · < dk die verschiedenen Tiefen der T1i -Folge. Konkatenation aller Bäume der Tiefe d1 , . . . , di ergibt Baum mit Tiefe di oder di + 1. Dessen Konkatenation mit Bäumen der Tiefe di+1 ergibt Baum der Tiefe di+1 oder di+1 + 1. Kosten O(di+1 − di ) X Insgesamt O( (di+1 − di )) 1≤i≤k−1 | . {z (d2 − d1 ) +(d3 − d2 ) +(d4 − d3 ) . . +(dk − dk−1 ). } (telescoping series) = O(dk − d1 ) = O(d(T )). . – Seite 316/726 3.5 B-Bäume oder Bayer-Bäume Verallgemeinerung der 2-3-Bäume. Knoten sind Seiten (pages) und können viele Daten enthalten. – Alle Zeiger, die einen Knoten verlassen, sind nil-Zeiger oder alle sind nicht nil-Zeiger. – Alle Blätter liegen auf einer Ebene. B-Baum der Ordnung m ≥ 3 – Jeder Knoten enthält höchstens m − 1 Daten. Ordnung m, da maximal m Zeiger einen Knoten verlassen. B-Bäume der Ordnung 3 sind 2-3-Bäume. – Jeder Knoten (mit Ausnahme der Wurzel) enthält mindestens dm/2e − 1 Daten. – Tiefe von B-Bäumen der Ordnung m mit n Daten ist mindestens dlogm (n + 1)e − 1 und höchstens blogdm/2e ((n + 1)/2)c. – Die Wurzel enthält mindestens ein Datum. – Die Daten im Knoten sind sortiert. – Knoten mit Daten x1 < · · · < xk haben k + 1 Zeiger für die Suchbereiche (v, x1 ), (x1 , x2 ), . . . , (xk−1 , xk ), (xk , w), wobei (v, w) der Suchbereich ist, den der Knoten repräsentiert. Beweis wie bei 2-3-Bäumen, aber bei der oberen Schranke muss die Sonderrolle der Wurzel beachtet werden. . – Seite 317/726 SEARCH, INSERT, DELETE greifen auf O(d(T )) Seiten konstant oft zu. . – Seite 318/726 INSERT(x) nach erfolgloser Suche und Speicherung des Suchpfades SEARCH(x): xv – Algorithmus offensichtlich, – nil-Zeiger → – eine Seite pro Ebene, O(logm n), – allgemein bis Ende der Rebalancierung: ein Zeiger auf einen Knoten v mit einem Datum y , alle Blätter dieses Teilbaums liegen eine Ebene tiefer als der Rest. – binäre Suche auf jeder Seite O(log m), – zusammen O(logm n · log m) = O(log n). – Ist v Wurzel des Baumes, gibt es keinen Rest, Rebalancierung abgeschlossen, Wurzel nimmt Sonderrolle wahr. . – Seite 319/726 . – Seite 320/726 z1 . . . zk 2. Fall: k = m − 1. y in den Knoten „hineinziehen“, m Daten a1 , . . . , am , Knoten „platzt“ k Daten y adm/2e 1. Fall: k < m − 1. y in den Knoten „hineinziehen“, Rebalancierung abgeschlossen. a1 . . . adm/2e−1 adm/2e+1 . . . am ... ... dm/2e − 1 Daten sind erlaubt und m − dm/2e = bm/2c ≥ dm/2e − 1 Problem eine Ebene nach oben verschoben. . – Seite 321/726 DELETE(x) nach erfolgreicher Suche und Abspeicherung des Suchpfades – Wenn x nicht in einem Blatt, vertausche x mit größtem Datum y < x. Korrektheit wie bisher. – Nun ist x in einem Blatt. – Entferne x und verschmelze die beiden zugehörigen nil-Zeiger. . – Seite 322/726 Ansonsten hat der betrachtete Knoten dm/2e − 2 Daten. 1. Fall: Ein direkter Geschwisterknoten hat mindestens dm/2e Daten → Datenrotation → Rebalancierung abgeschlossen. . . . x0 . . . . . . xk . . . – Hat der Knoten dann noch genügend Daten, sind wir fertig. (Ebenso, wenn das letzte Datum entfernt wurde.) x1 . . . xk . – Seite 323/726 y1 . . . ydm/2e−2 x1 . . . xk−1 x0 , y1 . . . ydm/2e−2 . – Seite 324/726 3.6 AVL-Bäume (AVL = Adel’son-Velskii und Landis (1962)) 2. Fall: Direkte Geschwisterknoten haben dm/2e − 1 Daten. Verschmelze die dm/2e − 2 Daten des betrachteten Knotens, die dm/2e − 1 Daten eines direkten Geschwisterknotens und das trennende Datum z im Elter zu neuem Knoten mit (dm/2e − 2) + (dm/2e − 1) + 1 ≤ 2dm/2e − 2 ≤ m − 1 Daten. Der Knoten übernimmt die 2dm/2e − 1 Zeiger der beiden Geschwisterknoten. Im Elter werden die beiden Zeiger, die z trennt, verschmolzen. → Problem eine Ebene nach oben verschoben. 2-3-Bäume – sind Basis der B-Bäume, – sind gut auf weitere Operationen erweiterbar (SPLIT, CONCATENATE), – haben worst case Zeiten von O(log n), aber – sie nutzen den Speicherplatz nicht gut. AVL-Bäume – haben worst case Zeiten von O(log n), – nutzen Speicherplatz besser, aber – sind nicht so gut auf weitere Operationen erweiterbar. . – Seite 325/726 Da Tiefe eines Baumes mit einem Knoten 0 ist, sei Tiefe eines leeren Baumes −1. . – Seite 326/726 ACHTUNG: Das bedeutet nicht, dass alle Blätter auf zwei Ebenen liegen. AVL-Baum ist – ein binärer Suchbaum (→ bessere Platzausnutzung als 2-3-Baum), – bei dem jeder Knoten einen Balancegrad bal(v) ∈ {−1, 0, +1} hat. v bal(v) T1 := d(T1 ) − d(T2 ). T2 . – Seite 327/726 . – Seite 328/726 Die Tiefe eines AVL-Baumes mit n Daten beträgt mindestens dlog(n + 1)e − 1 und 1 höchstens log(√5+1)−1 log n ≈ 1, 44 log n. Zur oberen Schranke: Die untere Schranke gilt für alle binären Bäume, da sie bei Tiefe d höchstens 1 + 2 + 22 + · · · + 2d = 2d+1 − 1 Daten enthalten können. Wie schon in den anderen Fällen gehen wir indirekt vor und berechnen die minimale Anzahl A(d) von Knoten in AVL-Bäumen der Tiefe d. Offensichtlich A(0) = 1 und A(1) = 2. d ≥ 2: 1 Wurzel v , 1 Teilbaum T 0 der Tiefe d − 1, 1 Teilbaum T 00 der Tiefe d0 ≤ d − 1, Da |bal(v)| ≤ 1, ist d0 ≥ d − 2. Wenn wir die Knotenzahl minimieren wollen: T 0 hat A(d − 1) Knoten, T 00 hat A(d − 2) Knoten. . – Seite 329/726 . – Seite 330/726 Also A(0) = 1, A(1) = 2, A(d) = 1 + A(d − 1) + A(d − 2). Nun Bekannter ist: Fib(0) = 1, Fib(1) = 1, Fib(d) = Fib(d − 1) + Fib(d − 2). Beweis: Auch bekannt: " √ Fib(k) = ≥ √1 5 √1 5 k+1 5+1 2 } | {z √≈1,68 k+1 5+1 2 − −1 . A(d) = Fib(d + 2) − 1. A(0) = 1 und Fib(2) − 1 = 2 − 1 = 1 A(1) = 2 und Fib(3) − 1 = 3 − 1 = 2 A(d) = 1 + A(d − 1) + A(d − 2) √ k+1 # 1− 5 2 } | {z Indvss. = 1 + Fib(d + 1) − 1 + Fib(d) − 1 = Fib(d + 2) − 1. ≈−0,68 . – Seite 331/726 . – Seite 332/726 Wenn ein AVL-Baum n Daten enthält und Tiefe d hat, gilt A(d) ≤ n, also Fib(d + 2) − 1 ≤ n. √ d+3 5+1 1 √ − 1 ≤ Fib(d + 2) ≤ n + 1 2 5 √ d+3 √ 5+1 ⇒ ≤ 5(n + 1) + 1 2 √ 1 √ log 5(n + 1) + 1 − 3 ⇒d≤ log 5+1 2 | {z } ≤ = √1 log( 5+1)−1 √1 log( 5+1)−1 log 6n − 3 log n + SEARCH(x) – wie in binären Suchbäumen O(log n). Zwischen log n und 1, 44 log n Vergleiche. Bei 2-3-Bäumen zwischen log3 n ≈ 0, 63 log n und log n Knoten, aber pro Knoten ein oder 2 Vergleiche. ≤6n 1 log 6 − 3 log( 5 + 1) − 1 {z } | √ ≤0 . – Seite 333/726 INSERT(x) nach erfolgloser Suche und Abspeicherung des Suchpfades – wie in binären Suchbäumen, aber Wir durchlaufen den Suchpfad rückwärts. Abbruchkriterium: Teilbaum, in den x eingefügt wurde, hat seine Tiefe nicht verändert. Ansonsten (wird sich zeigen) ist seine Tiefe um 1 gewachsen, das gilt sicher für den Elter des neuen Knotens, der x enthält. genau für die Knoten auf dem Suchpfad kann sich der Balancegrad geändert haben. Es ist bal(v) = 0 für den neuen Knoten. x . – Seite 334/726 Der auf dem Suchpfad erreichte Knoten heiße v . O. B. d. A. ist die Tiefe des rechten Teilbaumes um 1 gewachsen. v . – Seite 335/726 . – Seite 336/726 3. Fall: bal(v) = −1. 1. Fall: bal(v) = 1 (der alte Wert) v −→ d−1 d v d d alt 1. Unterfall: Suchpfad von v aus war rechts-rechts-. . . Das Problem kommt von außen. Also bal(v) := 0. Tiefe von T (v) unverändert Rebalancierung abgeschlossen. d A d d+1 d+2 v Also bal(v) := −1. Tiefe von T (v) um 1 ged+1 wachsen Rebalancierung am Elter fortsetzen. d d alt w w v Linksrotation 2. Fall: bal(v) = 0 −→ v neu w neu v v alt B C A −→ A B Die Tiefe von T (w) ist gleich der alten Tiefe von T (v). Rebalancierung abgeschlossen. neu z w w A d−2 d−1 d Rechts-Links- v neu z −→ Rechts an (w, z), C D w z v B D (v, w). (bal(v) := 0) (bal(z) := −1) Die Tiefe von T (w) ist gleich der alten Tiefe von T (v). Rebalancierung abgeschlossen. dann links an A B Rotation . – Seite 338/726 Also: bal(w) := 0 bal(v) := 1 bal(z) := 0 2. Unterfall: Suchpfad von v aus war rechts-links-. . . Das Problem kommt von innen. v C Also bal(w) := 0 bal(v) = 0. . – Seite 337/726 alt B C A B C D C Alternativ d(B) = d(C) + 1 . – Seite 339/726 . – Seite 340/726 Noch ein Sonderfall: w ist der eingefügte Knoten. v z nil w nil w −→ v nil z nil nil nil nil nil INSERT(4) bal(w) := 0 bal(v) := 0 bal(z) := 0 Rebalancierung abgeschlossen. 4 0 INSERT(5) INSERT(7) 4 -1 5 0 4 L -2 INSERT(2) 5 0 4 0 5 -1 7 0 Beachte: – Stets nur ein Teilbaum in der Tiefe gewachsen und zwar um +1. 7 0 5 – Nach einer Rotation oder Doppelrotation ist die Rebalancierung abgeschlossen. 4 1 – Um diese Stelle zu finden, muss evtl. der ganze Suchpfad zurückgelaufen werden. INSERT(1) 1 7 0 4 2 2 0 – Zeit O(1) pro Knoten auch auf dem Suchpfad. O(log n). 5 alter Wert 1 R 7 0 2 1 1 0 . – Seite 341/726 5 1 (wieder richtig) 2 0 1 0 INSERT(3) 2 4 0 LR 5 2 7 0 -1 1 0 DELETE(x) nach erfolgreicher Suche und Abspeicherung des Suchpfades 7 0 4 1 – genau wie in binären Suchbäumen (also evtl. das größte Datum y < x suchen, y und x austauschen, x entfernen). 3 0 4 INSERT(6) 0 3 0 1 0 7 0 RL alter Wert 0 2 0 5 -1 2 0 1 0 4 5 -2 3 0 7 – Wenn Entfernung aus dem Knoten u, ist unterhalb von u ein AVL-Baum, der eine Ebene aufsteigt. 1 – Also beginnt die Rebalancierung am Elter v von u. 6 0 4 1 0 – Er hat die Eigenschaft, dass die Tiefe eines Teilbaumes (o. B. d. A. des linken) gesunken ist und zwar um 1. 0 (wieder richtig) 2 0 . – Seite 342/726 6 0 3 0 5 0 7 0 . – Seite 343/726 . – Seite 344/726 3. Fall 1. Fall Alter Wert bal(v) = 1. bal(v) := 0. Die Tiefe von T (v) ist um 1 gesunken. Rebalancierung am Elter von v fortsetzen. Für ihn ist die Tiefe eines Teilbaumes um genau 1 gesunken. Alter Wert bal(v) = −1, neuer Wert wäre −2. Neue Situation v z w A 2. Fall Alter Wert bal(v) = 0. bal(v) := −1. Die Tiefe von T (v) ist unverändert. Rebalancierung abgeschlossen. d−2 B C D 1. Unterfall D endet auf Ebene d. Problem kommt von außen. bal(z) ∈ {0, −1} 2. Unterfall D endet nicht auf Ebene d. Problem kommt von innen. bal(z) = 1 . – Seite 345/726 War bal(z) = 0, dann bal(z) := 1, bal(v) := −1, bal(w) unverändert. Tiefeneu (z) = Tiefealt (v), Rebalancierung abgeschlossen. 1. Unterfall v z Linksrotation z w w A d−2 d−1 d A B C War bal(z) = −1, dann bal(z) := 0, bal(v) := 0, bal(w) unverändert. Tiefeneu (z) = Tiefealt (v) − 1, Rebalancierung am Elter fortsetzen. v an (v, z) D . – Seite 346/726 D B C . – Seite 347/726 . – Seite 348/726 Es gilt Tiefeneu (w) = Tiefealt (v) − 1 −→ Rebalancierung am Elter fortsetzen. 2.Unterfall w v Rechts-Links- z Rotation v z w A d−2 d−1 d D B A B C Neue Balancewerte: balalt (w) = 1 ⇒ bal(v) := 0, bal(w) := 0, bal(z) := −1. balalt (w) = 0 ⇒ bal(v) := 0, bal(w) := 0, bal(z) := 0. balalt (w) = −1 ⇒ bal(v) := 1, bal(w) := 0, bal(z) := 0. D C . – Seite 349/726 . – Seite 350/726 – Es kann mehrere (einfache oder doppelte) Rotationen geben. 3.7 Skiplisten Idee: Binäre Suche in linearen Listen erlauben. – Aufwand O(1) pro Knoten des Suchpfades −→ O(log n). Wie soll das gehen? – ABER: Komplexe Implementierung bei AVL-Bäumen und 2–3–Bäumen, viele Fallunterscheidungen. Moderne Lösung aus diesem Dilemma: Randomisierung. Verschieden lange Hilfslisten, jedes Element in der Liste auf Ebene 0, jedes zweite auf Ebene 1, . . . , jedes i-te auf Ebene 2i . Anfang 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Ende Suche hierarchisch von oben nach unten. . – Seite 351/726 . – Seite 352/726 Die Datenstruktur: INSERT und DELETE zerstören die regelmäßige Struktur. Welches Datum soll in wie vielen Listen vorkommen? – Die Daten sind Listen von oben nach unten verbunden. Dazu müssten wir die zukünftigen Operationen kennen! – Jedes Datum erhält eine Liste der durch Zufall bestimmten Länge. Lösung: Jedes Datum erhält – unabhängig von den anderen – eine zufällige Höhe H , d. h. es kommt auf den Ebenen 0, . . . , H − 1 vor. Prob(H = h) = 2−h . Wir erwarten also 50% der Daten nur auf Ebene 0, 25% Daten auf den Ebenen 0 und 1, . . . 100/2i % Daten auf den Ebenen 0, . . . , i − 1. Im Durchschnitt wie im Idealfall in der Abbildung. – Es gibt ein Anfangs- und ein Enddatum (Größe −∞ und +∞), deren Listenlänge der maximalen Listenlänge der aktuellen Daten entspricht. – Die Listenenden (Ebene 0) sind in sortierter Reihenfolge durch eine Liste verbunden, auf den höheren Ebenen werden die dort noch aktuellen Listen sortiert verbunden. Hoffnung: geringe Varianz und fast gleichmäßige Verteilung. . – Seite 353/726 . – Seite 354/726 INSERT(x) 13 2 3 8 21 35 8 5 54 89 – Suche (erfolglos) nach x− , einem Datum, das kleiner als x, aber größer als jedes andere Datum y < x ist. (Wird dabei x gefunden, war x schon gespeichert.) 95 SEARCH(x): – Beginne im obersten Feld des Anfangsdatums. – Suche in der aktuellen Liste, bis Datum gefunden (erfolgreiche Suche) oder größeres Datum gefunden. – Im zweiten Fall zurück zum letzten Datum kleiner als x. – Dort eine Ebene absteigen und weitersuchen. – Wird auf Ebene 0 ein größeres Datum gefunden, ist x nicht in der Datenstruktur (erfolglose Suche). . – Seite 355/726 – Speichere dabei alle Zeiger von Daten y < x zu Daten z > x. – Falls Höhe(x) > maximale Höhe der bisherigen Datenstruktur, erhöhe Anfangsdatum und Enddatum um genügend viele Ebenen und füge die neuen Zeiger zwischen ihnen zu den gespeicherten Zeigern hinzu. – Ersetze jeden gespeicherten Zeiger von y zu z durch Zeiger y → x und Zeiger x → z auf derselben Ebene. . – Seite 356/726 DELETE(x) CONCATENATE(L1 , L2 , L) – Suche (erfolglos) nach x− . – Erhöhe die Liste mit der kleineren Höhe auf die Höhe der anderen Liste. – Dabei werden alle Zeiger auf x entdeckt und gespeichert. – Wenn es auf einer Ebene einen Zeiger von y auf x und von x auf z gibt, ersetze diesen durch einen Zeiger von y auf z . – Ebenen mit Zeigern vom Anfangsdatum zum Enddatum können eliminiert werden. – Suche in L1 nach ∞− , um alle Zeiger auf das Enddatum zu finden. – Gibt es auf Ebene h in L1 einen Zeiger von x auf ∞ und in L2 einen Zeiger von −∞ auf y , ersetze diesen durch einen Zeiger von x auf y . – Das Enddatum von L1 und das Anfangsdatum von L2 werden eliminiert. . – Seite 357/726 . – Seite 358/726 SPLIT(L, a, L1 , L2 ) – Suche a und speichere alle Zeiger von y < a auf z > a. – Konstruiere Enddatum E1 von L1 und Anfangsdatum A2 von L2 mit der Höhe von L. – Korrektheit dieser Algorithmen offensichtlich. – Kaum Fallunterscheidungen. – Einfache Implementierung. – Alle Operationen werden unterstützt. – Die Zeiger y → z werden durch Zeiger y → E1 und A2 → z ersetzt. Wie steht es mit Speicherplatz und Rechenzeit? – Alle a verlassenden Zeiger werden ersetzt. Für a → z werden Zeiger a → E1 und A2 → z geschaffen. – L1 erhält als Anfangsdatum das Anfangsdatum von L und L2 als Enddatum das Enddatum von L. – Gibt es in L1 oder L2 Zeiger vom Anfang auf das Ende, können diese Ebenen abgebaut werden. . – Seite 359/726 . – Seite 360/726 Erwartete Höhe eines Datums (geometrische Verteilung, Erfolgswahrscheinlichkeit p, warte auf ersten Erfolg, hier p = 1/2, sei q = 1 − p) W.keit erster Erfolg im i-ten Versuch: q i−1 · p. X E(H) = i · q i−1 · p Zum Speicherplatz: H(n) : zufälllige Gesamthöhe einer Skipliste mit n Daten. Z(n) : zufällige Zeigeranzahl einer Skipliste mit n Daten. 1≤i<∞ = p · q0 Skiplisten sind gedächtnislos (memoryless), d.h. ihr Aussehen hängt nicht von Daten ab, die nicht mehr in ihr enthalten sind. +q 1 + q 1 +q 2 + q 2 + q 2 +q 3 + q 3 + q 3 + q 3 + . . .) 1 1 1 2 1 3 =p + · q + · q + · q + · · · = 1/p. p p p p Also erwartete Höhe ist 2. . – Seite 361/726 Prob(H(n) ≥ h) ≤ min{1, n/2h−1 }. . – Seite 362/726 E (H(n)) ≤ blog nc + 3. Die Schranke 1 ist trivial. Sei Ai das Ereignis „Höhe des i-ten Datums ist mindestens h“. Prob(Ai ) = 1/2h−1 . S Prob(H(n) ≥ h) = Prob(( Ai ) E (H(n)) = X 1≤h<∞ = Prob (H(n) = 1) + Prob (H(n) = 2) + Prob (H(n) = 3) + · · · +Prob (H(n) = 2) + Prob (H(n) = 3) + · · · +Prob (H(n) = 3) + · · · 1≤i≤n ≤ P 1≤i≤n h · Prob (H(n) = h) Prob(Ai ) = n · (1/2)h−1 . = Prob (H(n) ≥ 1) + Prob (H(n) ≥ 2) + Prob (H(n) ≥ 3) + · · · X = Prob (H(n) ≥ h) . 1≤h<∞ . – Seite 363/726 . – Seite 364/726 E(Z(n)) ≤ 2n + blog nc + 3 = O(n) Prob (H(n) ≥ h) ≤ 1 (benutzt für 1 ≤ h ≤ blog nc + 2) Summe: blog nc + 2. Prob (H(n) ≥ h) ≤ n/2h−1 n/2blog nc+2 ≤ Erwartungswert ist linear: für h ≥ blog nc + 3 n/2log n+1 weitere Summanden zusammen 1. Erwartete Anzahl von Zeigern, die Anfangsdatum verlassen: ≤ blog nc + 3 (= ˆ Höhe) = 1/2, 1 1 4, 8, . . . , Erwartete Anzahl von Zeigern, die i-tes Datum verlassen: 2 (= ˆ Höhe) . – Seite 365/726 . – Seite 366/726 Nun zur Rechenzeit. – Wir diskutieren nur erfolgreiche Schritte nach rechts und Schritte nach unten. x− – Erfolglose Suche nach nicht schneller als erfolgreiche Suche nach x. – Erfolglose Suche beginnt auf oberster Ebene und endet auf unterster Ebene. – Anzahl Schritte nach unten ist gleich der Höhe der Skipliste (inklusive des letzten Schritts von Ebene 0 „nach unten“). – INSERT, DELETE, CONCATENATE, SPLIT brauchen daher Zeit O(erfolglose Suche). – Wie wahrscheinlich ist ein Schritt nach rechts und wie wahrscheinlich ein Schritt nach unten??? – Also genügt es, die erfolglose Suche nach einem beliebigen Datum zu analysieren. – Die Suche wechselt zwischen akzeptierten Schritten nach rechts und nicht akzeptierten Schritten nach rechts, denen Schritte nach unten folgen. . – Seite 367/726 . – Seite 368/726 Ausweg: Rückwärtsanalyse (backward analysis) – Betrachte den Suchpfad rückwärts. – Daten werden stets auf ihrer höchsten Ebene erreicht. – Wenn Datum auf Rückwärtsweg erreicht wird, läuft der Rückwärtsweg von dort bis zur höchsten Ebene des Datums. – Wenn Datum auf Ebene h existiert, ist die W.keit, dass es auf Ebene h + 1 existiert, genau 1/2. → Rückwärtsweg ist besser zu analysieren. Wie viele Schritte nach links erwarten wir, bevor wir einen Schritt nach oben machen? i+1 W.keit (i Schritte nach links, dann nach oben) = 12 . P P i+1 i Erwartungswert: i · 12 = 12 i · 12 = 12 · 2 = 1. 1≤i<∞ 1≤i<∞ siehe E-wert geometr. Verteilung Aufstieg bis Ebene dlog ne + 1: – dlog ne + 1 Schritte nach oben, – erwartet dlog ne + 1 Schritte nach links (E-wert ist linear). 1/2 O(log n). 1/2 . – Seite 369/726 Analyse der höheren Ebenen ( 1 Datum i hat eine Höhe von mind. dlog ne + 1 Sei Xi = 0 sonst. Prob(Xi = 1) = 1 2dlog ne+1−1 Skiplisten sind elegant und effizient. X := X1 + · · · + Xn Anzahl der Daten mit großer Höhe. P P P 1 E(X) = E( Xi ) = E(Xi ) ≤ n = 1. 1≤i≤n Die erwartete Zeit für jede der betrachteten Operationen auf Skiplisten ist O(log n). Noch mehr Wahrscheinlichkeitstheorie: sogar mit sehr großer W.keit ist die Rechenzeit O(log n). ≤ n1 . E(Xi ) = 0 · Prob(Xi = 0) + 1 · Prob(Xi = 1) ≤ n1 . 1≤i≤n . – Seite 370/726 Randomisierung ist ein Schlüsselkonzept der Informatik. 1≤i≤n Jedes Datum auf dieser Höhe führt zu maximal einem Schritt nach links. Erwartete Höhe ab Ebene dlog ne + 1 ist ≤ 3. O(1). . – Seite 371/726 . – Seite 372/726 4 Sortieren 4.1 Vorbemerkungen Wie bewerten wir Sortieralgorithmen? – Historisch: Viel CPU-Zeit für das Sortieren von Daten. – Über kein Informatikproblem gibt es mehr Literatur. – Für „alle“ neuen algorithmischen Konzepte war Sortieren eines der ersten Beispiele. – Jede(r) muss die wichtigsten Sortieralgorithmen kennen, obwohl sie niemand mehr selber implementieren muss. – Wesentliche Vergleiche sind Vergleiche zwischen Daten, sie dienen als zentrales Maß (gut zu handhaben, gut für Vergleiche verschiedener Algorithmen, wenn Daten komplex (Bäume mit Inorder-Nummerierung) sind, dann sind diese Vergleiche komplex). – Weitere Operationen (Vergleiche von Indizes, Zuweisungen, arithmetische Operationen). Ziele: Kleine Zahl wesentlicher Vergleiche und Anzahl weiterer Operationen nur um konstanten Faktor größer. . – Seite 373/726 Sortierverfahren arbeiten . – Seite 374/726 Sortierverfahren heißen stabil, wenn Daten mit gleichem Wert in der gegebenen Reihenfolge bleiben (sie sind z. B. bzgl. eines Sekundärkriteriums bereits sortiert). – intern, d. h. alle Daten stets verfügbar, und sogar – in situ (am Platz), wenn die Daten in einem Array gegeben sind und kaum Extraplatz (z. B. O(log n)) benötigt wird, oder Sortierverfahren heißen adaptiv bzgl. eines Sortiertheitsmaßes, wenn schon „fast sortierte“ Datenfolgen besonders schnell sortiert werden. – extern (bei riesigen Datenmengen, von denen die meisten stets ausgelagert sind). . – Seite 375/726 . – Seite 376/726 Allgemeine Sortieralgorithmen sind auf Daten a1 , . . . , an aus beliebigen vollständig geordneten Mengen anwendbar: Effizientes Sortieren spezieller Daten: – Bucketsort (Kap. 4.7). – Insertionsort (Kap. 4.2), Spezielle Sortierprobleme: Auswahlproblem, bestimme das Datum mit Rang k (in der sortierten Folge an Position k ): – Mergesort (Kap. 4.3), – Quicksort (Kap. 4.4), – Quickselect (Kap. 4.8). – Heapsort (Kap. 4.5), – Komplexität des allgemeinen Sortierproblems (Kap. 4.6). Sortieren auf Parallelrechnern (n Prozessoren, die Daten vergleichen können): – Batchersort (Kap. 4.9). . – Seite 377/726 . – Seite 378/726 Vorab ein Experiment: 4.2 Insertionsort — Sortieren mit binärer Suche Wie sollte man 200 Karteikarten sortieren, die jeweils Wörter der Länge 3 enthalten? Idee: Eine Datenstruktur, die SEARCH, INSERT und LIST effizient unterstützt −→ Kapitel 3. Aber in situ? Wir benutzen ein Array, das SEARCH und LIST unterstützt. Nur bei SEARCH sind wesentliche Vergleiche nötig. Daher sollte die Anzahl wesentlicher Vergleiche klein werden. Suche in geordneten Arrays: Binäre Suche. . – Seite 379/726 . – Seite 380/726 Wir zerlegen das Array gedanklich: a1 Einfügen von ai+1 : ai ai+1 bereits sortiert – Binäre Suche nach a+ i+1 im vorderen Teil des Arrays der Länge i. (Wieder ist a+ i+1 größer als ai+1 , aber kleiner als jedes andere Datum größer als ai+1 .) an noch nicht betrachtet – Die Suche ist erfolglos und findet eine Lücke, auf die aj , . . . , ai folgen. Zu Beginn: i = 1. Falls i = n, Array sortiert. – Entferne ai+1 , für k = i, . . . , j verschiebe ak um eine Position nach rechts, füge ai+1 an Position j ein. . – Seite 381/726 Warum a+ i+1 ? Genauer mit der Stirling-Formel: √ Dadurch erhalten wir einen stabilen Sortieralgorithmus. 1≤i≤n−1 dlog(i + 1)e = 2≤i≤n dlog ie ≤ P −→ 1 ≈1,326 ≤ dlog(i + 1)e beim Einfügen von ai+1 , 1 ≤ i ≤ n − 1. P n! 2π nn+1/2 e−n für n → ∞. √ Also log(n!) ≈ log( 2π) +(n + 1/2) log n − n log e | {z } | {z } Anzahl wesentlicher Vergleiche: Insgesamt P ≤ . – Seite 382/726 ∈[1,4426;1,4427] ≈ n log n − 1, 4427n + 1/2 log n + O(1). Anzahl wesentlicher Vergleiche bei Insertionsort: (log i + 1) ≤ n log n − 0, 4427n + O(log n). 2≤i≤n ≤ log(n!) + n ≤ log(nn ) + n = n log n + n. . – Seite 383/726 . – Seite 384/726 Wie teuer ist der Datentransport? 4.3 Mergesort — Sortieren durch Mischen Zähle die Schritte, bei denen ein Datum nach rechts rückt. Mischen eine schlechte Übersetzung von „to merge“ — besser verschmelzen (keinesfalls Mischen von Spielkarten). best case: 0. worst case: 1 + 2 + · · · + (n − 1) = ai+1 . n(n−1) , 2 also i bei Datum average case, wenn a+ i+1 alle Lücken mit derselben W.keit trifft (z. B. bei zufälliger Permutation paarweise verschiedener Daten). Bei ai+1 gibt es i + 1 Lücken, die 0, 1, . . . , i Datentransporte 1 (0 + 1 + · · · + i) = i/2, also erfordern, im average case: i+1 die Hälfte des worst case. Insgesamt MERGE(a1 , . . . , ak ; b1 , . . . , bm ): Sortiere a1 , . . . , ak , b1 , . . . , bm , wobei bekannt ist, dass a1 ≤ · · · ≤ ak und b1 ≤ · · · ≤ bm ist. Reißverschlussverfahren mit höchstens k + m − 1 wesentlichen Vergleichen und kaum Extraaufwand. n(n−1) . 4 . – Seite 385/726 . – Seite 386/726 Rekursive Beschreibung n = 2k MERGESORT(a1 , . . . , an ) – n=1: nichts zu sortieren. – n>1: (b1 , . . . , bdn/2e ) :=MERGESORT(a1 , . . . , adn/2e ), (c1 , . . . , cbn/2c ) :=MERGESORT(adn/2e+1 , . . . , an ), (d1 , . . . , dn ) :=MERGE(b1 , . . . , bdn/2e ; c1 , . . . , cbn/2c ). Rufe an den Knoten MERGE auf. a1 a2 a3 Anzahl wesentlicher Vergleiche im worst case: – V (1) = 0, a4 an−3 an−2 an−1 an – V (n) = V (dn/2e) + V (bn/2c) + n − 1. . – Seite 387/726 . – Seite 388/726 Lösung der Rekursionsgleichung Zwei Arrays genügen bei iterativer Variante nur für n = 2k → analog zur Analyse des Divide-and-Conquer Verfahrens zum Maxsummenproblem. n = 2k Ergebnis: n log n − n + 1 und wenig Extraaufwand. Insgesamt viel effizienter als Insertionsort. Nach Runde i : Blöcke der Länge 2i sortiert, zu Beginn i = 0. 2i 2i 2i 2i B1 B2 B3 B4 Aber: Reißverschlussverfahren nicht in situ. Runde i + 1 B1 B2 2i+1 2i+1 Runde i + 2 erfolgt von „unten nach oben“. . – Seite 389/726 . – Seite 390/726 Gut geeignet für externes Sortieren. Warum starten wir mit Blöcken der Länge 1? Nur gerade zu bearbeitende Teilblöcke zweier sortierter Blöcke müssen verfügbar sein. Lauf (run): Nicht verlängerbarer sortierter Teilblock der Eingabe. – Einteilung der Läufe mit n − 1 Vergleichen. Stabilität beim Reißverschlussverfahren leicht zu garantieren. – l Läufe: dlog le Runden, die die Blockzahl halbieren. – Anzahl an wesentlichen Vergleichen: ≤ n − 1 + dlog le · (n − 1). . – Seite 391/726 . – Seite 392/726 Problemzerlegung 4.4 Quicksort Zerlegungsdatum Mergesort und Quicksort sind bei rekursiver Beschreibung Divide-and-Conquer Verfahren. ai Mergesort: Problemzerlegung fast umsonst, Arbeit beim Zusammenfügen der Teillösungen. Quicksort: Arbeit für die Erzeugung der Problemzerlegung, praktisch keine Arbeit beim Zusammenfügen der Teillösungen. nur Daten ≤ ai | {z Sortieren nur Daten ≥ ai ai } | {z Sortieren } ai Das Array ist sortiert. Ziel: Jedes Datum aj , j 6= i, einmal mit ai vergleichen. . – Seite 393/726 Aber: Wie macht man das in situ? . – Seite 394/726 3.) Updating – Entferne a∗ = ai aus Array (→ Zwischenspeicherung). – Parameter: i – momentan freie Position, l – linkeste noch nicht untersuchte Position l ≤ i − 1, eventuell l = nil, zu Beginn l := 1 oder l := nil, falls i = 1. r – rechteste noch nicht untersuchte Position r ≥ i + 1, eventuell r = nil, zu Beginn r := n oder r := nil, falls i = n. 1.) Von Position l beginnend suche das kleinste l0 ≤ i − 1 mit al0 < ai , eventuell l0 = nil. – l0 6= nil, r0 6= nil: Tausche al0 und ar0 aus, l := l0 + 1, falls l0 + 1 < i, sonst nil, r := r 0 − 1, falls r0 − 1 > i, sonst nil. – l0 6= nil, r0 = nil: Transportiere al0 an Position i, r := i − 1, falls i − 1 > l0 , sonst nil, i := l0 , l := nil. – l0 = nil, r0 = 6 nil: Analog. – l0 = nil, r0 = nil: Speichere a∗ an Position i, Ende der Phase. Jedes Element aj , j 6= i, wird genau einmal mit ai verglichen. 2.) Von Position r beginnend suche das größte r 0 ≥ i + 1 mit ar0 > ai , eventuell r0 = nil. . – Seite 395/726 . – Seite 396/726 i = 7, a∗ = ai = 53 extern gespeichert, l = 1, r = 13, X bezeichnet die leeren Arraypositionen. Strategien zur Wahl des Zerlegungsdatums Position 1 2 3 4 5 6 7 8 9 10 11 12 13 Daten 15 47 33 87 98 17 X 76 83 2 53 27 44 l0 = 4, r 0 = 13 44 87 l = 5, r = 12, l0 = 5, r 0 = 12 27 0 98 0 l = 6, r = 11, l = nil, r = 10 2 0 X Strategie 1: i ist die vorderste Position des betrachteten Bereichs. – Problemzerlegung in Größe n − 1 und 0 möglich. Anzahl wesentlicher Vergleiche, wenn sich dies wiederholt: (n − 1) + (n − 2) + · · · + 2 + 1 = n2 = 12 n2 − 12 n. Alle Datenpaare werden verglichen, schlimmer kann es nicht kommen. 0 l = 8, i = 10, r = 10, l = 8, r = nil X 0 76 0 r = 9, i = 8, l = 8, l = nil, r = nil 53 . – Seite 397/726 Average case Analyse . – Seite 398/726 Zu Beginn – zufällige Auswahl von Positionen für 1, . . . , i − 1, Durchschnitt von was? Jede Permutation von 1, . . . , n hat Wahrscheinlichkeit 1/n!, Eingabe zu sein. V (n): durchschnittliche Anzahl wesentlicher Vergleiche. V (0) = V (1) = 0. – bei Auswahl der Positionen p1 , . . . , pi−1 landet Datum von pk an Position jk ∈ {1, . . . , i − 1} und zwar unabhängig von seinem Wert, da nur Vergleiche mit Datum i, – also hinterher alle (i − 1)! Reihenfolgen auf den Positionen 1, . . . , i − 1 mit Wahrscheinlichkeit 1/(i − 1)!. Zerlegungsdatum i: – alle Daten j < i werden in Phase 1 gleich behandelt, Analog für die Positionen i + 1, . . . , n. – ebenso alle Daten k > i. . – Seite 399/726 . – Seite 400/726 Prob(Zerlegungsdatum hat Wert i) = n1 . Analoge Rechnung → Also: n − 1 wesentliche Vergleiche in Phase 1 W.keit 1/n Probleme der Größe 0 und n − 1, dann 1 und n − 2, ” ≈ 1, 386n log n − 2, 846n. n − 1 und 0. ” Also für n ≥ 2: V (n) = n − 1 + V (n) = 2 · (n + 1) · H(n) − 4n Guter Fall: Zerlegungsdatum landet an Position dn/2e → 1 X (V (j − 1) + V (n − j)). n V 0 (n) = n − 1 + V 0 (dn/2e − 1) + V 0 (bn/2c) 1≤j≤n ≈ n log n ± O(n). Wir kennen: T (n) = n + 1 X (T (j − 1) + T (n − j)). n (H(n) ≈ ln n + 0, 577...) 1≤j≤n . – Seite 401/726 . – Seite 402/726 Ziel: Zerlegungsdatum „mittiger“. Strategie 2: Wähle i ∈ {1, dn/2e, n}, so dass ai Median von a1 , adn/2e , an . vorderste mittlere Da Zerlegungsdatum bereits mit 2 Daten verglichen, nur noch n − 3 weitere wesentliche Vergleiche in Phase 1. Problemzerlegung in Größe n − 2 und 1 möglich. hintere Position Anzahl wesentlicher Vergleiche, wenn sich das wiederholt: Median (x, y, z) – vergleiche x, y – vergleiche x, z – vergleiche y, z , falls nicht y ≤ x ≤ z oder z ≤ x ≤ y . Worst case: 3 wesentliche Vergleiche bei drei verschiedenen Daten 2 von 6 Möglichkeiten. n + (n − 2) + (n − 4) + (n − 6) + · · · n n n =2· + −1 + − 2 + ··· + 1 falls n gerade 2 2 2 n n +1 = · 2 2 1 2 1 = n + n ≈ 50% gegenüber Strategie 1. 4 2 Average case: 8/3 wesentliche Vergleiche. . – Seite 403/726 . – Seite 404/726 Anzahl der Möglichkeiten mit i2 = j : Average case Analyse 8 1 3 + n − 3 = n − 3 wesentliche Vergleiche in Phase 1. Wenn Zerlegungsdatum i, dann links Gleichverteilung der Permutationen von 1, . . . , i − 1, rechts analog für i + 1, . . . , n. Aber welche Problemgrößen mit welcher W.keit in Phase 2? – i1 ∈ {1, .., j − 1} – i3 ∈ {j + 1, . . . , n} – also (j − 1) · (n − j) Möglichkeiten Prob(Zerlegungsdatum ist j) = Die drei gewählten Daten bilden eine zufällige Teilmenge von {1, ..., n}, jedes {i1 , i2 , i3 } mit i1 < i2 < i3 hat W.keit 1/ n3 . (j − 1) · (n − j) . n 3 Also W (0) = 0, W (1) = 0, W (2) = 1 und für n ≥ 3 X 1 W (n) = n− + 3 2≤j≤n−1 (j − 1) · (n − j) ·(W (j − 1) + W (n − j)) . n 3 . – Seite 405/726 −→ (komplexe Rechnung) für n ≥ 6: W (n) = 12 7 · (n + 1) · H(n − 1) − ≈ 1, 188n log n − 2, 255n. . – Seite 406/726 Strategie 3: Wähle Position i ∈ {1, . . . , n} zufällig gleichverteilt. 477 223 147 n + 147 + 252 147 · Strategie 4: Wähle gleichverteilt drei verschiedene Positionen i1 , i2 , i3 ∈ {1, . . . , n} und wähle i so, dass ai = Median(ai1 , ai2 , ai3 ). 1 n Also besser: Daher Name Clever Quicksort. Worst case: Strategie 3 wie Strategie 1, Strategie 4 wie Strategie 2. . – Seite 407/726 . – Seite 408/726 Average case Sind also Strategie 1 und Strategie 3 gleich gut? Durchschnitt von was? Nein, es sind „verschiedene Durchschnitte“. – Beliebige Reihenfolge von n verschiedenen Daten. – Durchschnitt bezogen auf die Zufallsbits des Algorithmus bei der Wahl des Zerlegungsdatums. Analyse: Strategie 3 wie Strategie 1, Strategie 4 wie Strategie 3. Strategie 1: Durchschnitt bezüglich einer Verteilung auf der Menge der Eingaben – ob die Eingaben gemäß dieser Verteilung entstehen ??? Strategie 3: Durchschnitt bezüglich der Zufallsbits des Rechners – deren Qualität können wir kontrollieren !!! Viel besser. Analog: Strategie 2 ↔ Strategie 4. . – Seite 409/726 . – Seite 410/726 Implementierungstricks: Üblich: Implementierung als rekursiver Algorithmus. – Spare Indexvergleiche und Vertauschungen von Daten. Extraplatz: 1 für a∗ , Platz für Rekursionsstack. – Schalte bei „kleinen“ Teilproblemen (etwa Größe 10) auf Insertionsort um (spart Verwaltungsaufwand). Links vor rechts: worst case Θ(n). In der Praxis üblich. Groß vor klein: worst case Θ(log n). Theoretisch besser. Quicksort in Anwendungen am häufigsten eingesetzt. . – Seite 411/726 . – Seite 412/726 4.5 Heapsort Turnierform 7 – Bestimme kleinstes Datum. – Bestimme kleinstes Datum von Rest, bis Rest leer. 19 24 24 48 19 minimal → Ausgabe 7 19 Aber: Erzielte Informationen nutzen. 7 23 7 19 7 ist 63 19 24 Update bottom-up 90 101 63 63 19 24 48 19 90 23 63 90 101 63 ≤ dlog ne Vergleiche – Jedes Datum möglichst nur einmal speichern. – Alle Daten in einem Array und nicht in einem Baum. – Kein Extraarray für die Ausgabe. → Heapsort . – Seite 413/726 Binäre Bäume ohne Zeiger Arraypositionen ebenenweise von links nach rechts, letzte Ebene nicht notwendigerweise voll. 1 2 8 16 5 9 17 18 10 6 11 12 – Wie wird aus einem Array ein Heap? 7 13 Das Array mit den Daten a1 , . . . , an ist ein (Min-) Heap, wenn gilt ai ≤ a2i oder 2i > n, ai ≤ a2i+1 oder 2i + 1 > n. Offensichtlich: a1 = min{a1 , . . . , an }. 3 4 . – Seite 414/726 14 – Wie wird mit Hilfe eines Heaps sortiert? 15 → Heaps bilden Datenstrukturen mit weiteren Anwendungen. 19 20 Kinder von i an den Position 2i und 2i + 1 (bzw. nil, falls ≥ n). Elter von i an Position bi/2c (bzw. nil, falls i = 1). . – Seite 415/726 . – Seite 416/726 Prozedur zur Heapreparatur Heapsort reheap(i, m) betrachtet den Teilbaum T mit Wurzel i und darin nur die Positionen p ≤ m. Heap Creation Phase: Für i := bn/2c, . . . , 1: reheap(i, n). Voraussetzung: Die Heapeigenschaft ist in T überall erfüllt mit der eventuellen Ausnahme der Wurzel. Alle Positionen p > bn/2c haben keine Kinder und sind daher Wurzeln von Heaps. Wurde reheap(i, n) für i := bn/2c, . . . , j + 1 durchgeführt, sind die Kinder von j Wurzeln von Heaps und die Voraussetzungen an reheap(j, n) erfüllt. Ziel: T enthält einen Heap mit denselben Daten. Realisierung von reheap später. Schließlich erzeugt reheap(1, n) einen Heap. . – Seite 417/726 . – Seite 418/726 Für m := n, . . . , 2: vertausche a(1) und a(m), reheap(1, m − 1). Realisierung von reheap Selection Phase Wurzeldatum raus alle Daten auf dem Pfad um 1 nach oben Ex-Wurzeldatum unten rein Wird die Zeile mit Wert m aufgerufen, gilt a(m + 1) ≥ · · · ≥ a(n) und dies sind die kleinsten n − m Daten. Das Array a(1), . . . , a(m) ist ein Heap, also a(1) minimal. Nach Voraussetzung a(m) ≥ · · · ≥ a(n) und dies sind die kleinsten n − m + 1 Daten. ? Heapeigenschaft in a(1), . . . , a(m − 1) nur an Wurzel verletzt, Reparatur durch reheap(1, m − 1). x x Wer darf aufsteigen? y Das Kleinere der Daten! ? y x ≤ y für Heap Also betrachten wir den Pfad der kleineren Kinder. . – Seite 419/726 . – Seite 420/726 Wo auf diesem Pfad passt das Wurzeldatum x hin? Suchstrategie 1: Die klassische Strategie – Verfolge beide Ziele gleichzeitig. – Bestimme das kleinere Kind ui . u x y – Vergleiche es mit dem Wurzeldatum x. ≤x≤y (es gilt y ≤ z , da y auf dem Pfad Bedingung u – Stoppe, wenn x ≤ ui . Sonst – Alle u1 , . . . , ui−1 wandern nach oben, x an die Position von ui−1 . der kleineren Kinder) z Also u1 ≤ u2 ≤ · · · ≤ um auf dem Pfad der kleineren Kinder, finde i mit ui ≤ x ≤ ui+1 . – Anzahl Vergleiche ≈ 2i, auf letzter Ebene wird in jedem Fall gestoppt, dort vielleicht nur ein Kind. . – Seite 421/726 . – Seite 422/726 Anzahl der Vergleiche bei Baumtiefe d: Strategienklasse 2 – Berechne Pfad der kleineren Kinder. – Speichere die Positionen in einem Array oder speichere nur die Endposition p und berechne ggf. die Positionen als bp/2j c. – Suche auf dem Pfad die passende Stelle. – Pfad der kleineren Kinder hat Länge d − 1 oder d, ≈ d. – Richtige Position auf Ebene i. binär: d + log d. linear bottom-up: d + (d − i + 1) ≈ 2d − i. klassisch: 2i. 2d − i ≤ 2i ⇔ i ≥ 23 d. binäre Suche geometrische Suche bottom-up lineare Suche bottom-up . – Seite 423/726 . – Seite 424/726 Analyse der Heapsortvarianten Welchen Wert erwarten wir für i? Wie tief sind die in den reheap-Aufrufen betrachteten Teilbäume? Summe dieser Tiefen? Selection Phase Summe der Baumtiefen ist kleiner als n log n. ← 87,5% der Denn n − 1 Aufrufe, Tiefe des Gesamtbaumes blog nc. (Man kann zeigen: n log n − O(n).) Daten auf den letzten 3 Ebenen In Selection Phase ist x ehemaliges Blattdatum, also tendenziell groß. Binär: bester worst case. Linear bottom-up: meistens am besten. . – Seite 425/726 Knoten Tiefe ≥ 0 (gar nicht betrachtet) – 1, . . . , n: – 1, . . . , bn/2c: Tiefe ≥ 1 Anzahl bn/2c – 1, . . . , bn/4c: Tiefe ≥ 2 bn/4c .. . Heap Creation Phase Summe der Baumtiefen ist kleiner als n. (Dies ist wichtig für Heaps als Datenstruktur.) P Tiefe des Baumes mit Wurzel i 1≤i≤bn/2c = P 1≤d≤blog nc = P . – Seite 426/726 – d· Anzahl der Bäume mit Tiefe genau d ein Beitrag der Größe d ∗ Baum der Tiefe d∗ d∗ Beiträge, 1 ≤ d ≤ d∗ , der Größe 1 1, . . . , bn/2d c: Tiefe ≥ d Summe P der Baumtiefen P ≤ bn/2d c < n/2d = n. 1≤d≤blog nc Anzahl der Bäume mit Tiefe mindestens d bn/2d c 1≤d<∞ 1≤d≤blog nc . – Seite 427/726 . – Seite 428/726 Die klassische Variante Binär bottom-up Weniger als 2n log n + 2n wesentliche Vergleiche. Höchstens n log n + n log log n + 3n wesentliche Vergleiche. (Bei binärer Suche kann im best case nur wenig eingespart werden.) Pro Ebene in reheap-Aufrufen höchstens 2 wesentliche Vergleiche. n log n + n für die Bestimmung des Pfades der kleineren Kinder, ≤ n (großzügig für die bottom-up Suche der Heap Creation Phase), ≤ dlog(blog nc + 1)e ≤ log log n + 1 für jede bottom-up Suche in der Selection Phase. Aber auch: Gegeben eine zufällige Permutation von 1, . . . , n: avarage case Anzahl an Vergleichen: 2n log n − O(n). . – Seite 429/726 . – Seite 430/726 Ziel: 12 n log n + O(n) wesentliche Vergleiche in den bottom-up Suchen der Selection Phase, Linear bottom-up Wir betrachten nur Spezialfall n = 2k . O. B. d. A. Datenmenge {1, . . . , n}. Es genügt zu zeigen: 14 n log n + O(n) wesentliche Vergleiche für die ersten n/2 bottom-up Suchen der Selection Phase. Höchstens 32 n log n + O(n) wesentliche Vergleiche (und n log n + O(n) im average case). Beweis der worst case Schranke Dann insgesamt: – n log n + n für die Pfade der kleineren Kinder, – n für die bottom-up Suche in der Heap Creation Phase. X 0≤i≤k−1 X 1 1 ( n/2i log n/2i +c · n/2i ) ≤ 1/2i n log n | {z } 4 4 0≤i<∞ ≤log n + c·n = . – Seite 431/726 X 1/2i 0≤i<∞ 1 n log n + 2cn. 2 . – Seite 432/726 m-te bottom-up Suche: Wurzeldatum landet auf Ebene d(m), dann ≤ k − d(m) wesentliche Vergleiche in der bottom-up Suche. Große Daten: n/2 + 1, . . . , n. Kleine Daten 1, . . . , n/2. Falls > n/4 Blätter klein, haben diese ≥ n/8 kleine Eltern, diese ≥ n/16 kleine Eltern, ... → es gibt > n/2 kleine Daten, Widerspruch. Große Daten an den Blättern werden nur von großen Daten verdrängt. → ≥ n/4 bottom-up Suchen mit großen Daten, betrachte genau n/4 davon. → Die anderen n/4 bottom-up Suchen kosten maximal n 4 log n wesentliche Vergleiche. Mindestens n/4 Blätter sind groß. . – Seite 433/726 Neues Ziel: n/4 bottom-up Suchen mit großen Daten kosten nur O(n) wesentliche Vergleiche. Entscheidend: Die großen Daten sind nach Abschluss der ersten n/2 Runden der Selection Phase noch im Heap. Kann ein Datum mehrfach Wurzeldatum werden? Ja, aber nur, wenn es vorher im Blatt gelandet ist, also nur einen wesentlichen Vergleich verursacht hat. → Summe der d(m)-Werte ist mindestens so groß wie die Summe der Tiefen von n/4 Daten im Baum. . – Seite 435/726 . – Seite 434/726 Die Summe ist am kleinsten, wenn die Ebenen 0, . . . , k − 3 voll besetzt sind und Ebene k − 4 ein Datum enthält. Dann ist die Summe X i · 2i +(k − 2) · 1 ≥ (k − 4) · 2k−2 0≤i≤k−3 | {z } =(k−4)·2k−2 +2 (siehe Beispiel 1.6.4) = (k − 4) · n . 4 Also Summe aller k − d(m) ≤k· n n − (k − 4) · = n. 4 4 (Der Faktor 3/2 beschreibt für den worst case sogar die Wahrheit.) . – Seite 436/726 4.6 Eine untere Schranke für allgemeine Sortierverfahren Ordnungstyp einer Eingabefolge Können wir die n log n-Barriere durchbrechen? a1 a2 a3 a4 hat Ordnungstyp 4 2 3 5 wenn sortierte Folge a5 a2 a3 a1 Nicht für allgemeine Sortierverfahren! Erinnerung: Ein Sortierverfahren heißt allgemein, wenn es auf beliebigen geordneten Mengen M arbeitet, d. h. auf Daten aus M ist nur der „≤-Test“ definiert, keine Addition, ... Wir beschränken uns auf das Sortieren paarweise verschiedener Daten. a5 1 , a4 . Alle n! Ordnungstypen sind möglich. Ist die sortierte Folge bekannt, lässt sich daraus der Ordnungstyp berechnen. Sortieren heißt, die Anzahl möglicher Ordnungstypen auf 1 zu reduzieren. . – Seite 437/726 . – Seite 438/726 Entscheidungsbäume – Abstraktionen von allgemeinen Sortieralgorithmen (1, 3) Knoten beschreiben Menge noch möglicher Ordnungstypen und den nächsten Vergleich. Der Vergleich hat 2 Antworten, der Knoten 2 Kinder („ai < aj ?“ – linkes Kind für „ja“ rechtes Kind für „nein“). 123 123 An den Blättern ist die Menge einelementig (– oder leer). 132 132 123 (1, 2) 213 (2, 3) 132 213 123 132 (2, 3) 231 231 (1, 2) 312 213 231 312 312 321 321 312 321 321 → mindestens n! Blätter. . – Seite 439/726 . – Seite 440/726 Was können wir am Entscheidungsbaum ablesen? – Rechenzeit für Ordnungstyp π : Länge des Weges von der Wurzel zum Blatt für π . – Worst case Rechenzeit: Länge des längsten Weges zu einem Blatt für einen Ordnungstyp. – Average case Rechenzeit (bei Gleichverteilung auf den n! Ordnungstypen): durchschnittliche Länge der n! Wege zu den n! Blättern für die Ordnungstypen. Die Tiefe eines Binärbaums mit N Blättern beträgt mindestens dlog N e. Bei Tiefe höchstens d die meisten Blätter im vollständigen Binärbaum die Tiefe d, dann 2d Blätter. Also 2d ≥ N und d ≥ dlog N e. . – Seite 441/726 . – Seite 442/726 Beweis durch Widerspruch: Die durchschnittliche Tiefe der Blätter eines Binärbaums mit N Blättern beträgt mindestens dlog N e − 1. d0 Sei T Binärbaum mit N Blättern und minimaler durchschnittlicher Tiefe der Blätter. u u w x mind. 2 → Blätter liegen auf zwei benachbarten Ebenen. v v d w x Hat v nur ein Kind w, ist durchschnittliche Tiefe nicht minimal (streiche w, v wird Blatt). . – Seite 443/726 . – Seite 444/726 Tiefe(u) + Tiefe(w) + Tiefe(x) = d0 + 2d Seien dies nun die Ebenen D − 1 und D. Tiefe(w) + Tiefe(x) +Tiefe(v ) = 2(d0 + 1) + d − 1 Falls D ≤ dlog N e − 1, hat der Binärbaum keine N Blätter. Behauptung: 2(d0 + 1) + d − 1 < d0 + 2d Also D ≥ dlog N e und alle Blätter haben Mindesttiefe dlog N e − 1. Das ist äquivalent zu d0 + 1 < d. Anwendung N = n! Das ist nach Voraussetzung (d − d0 ≥ 2) wahr. worst case dlog(n!)e ≈ n log n − 1, 4427n average case dlog(n!)e − 1. . – Seite 445/726 . – Seite 446/726 Bilanz Insertionsort Vergleiche Vergleiche Sonstige Extra- worst case average case Operationen platz Bottom-up n log n + n log log n kaum weniger O(n log n) O(1) Heapsort +3n Vergleiche Vergleiche Sonstige Extra- worst case average case Operationen platz n log n − 0, 443n kaum weniger Θ(n2 ) O(1) n2 /2 1, 386n log n O(#Vergleiche) O(log n) Suche O(#Vergleiche) O(log n) Heapsort oder O(log n) mit binärer Quicksort −2, 846n Bottom-up Clever n2 /4 −2, 255n Quicksort Heapsort 1, 188n log n 2n log n + O(n) 2n log n ± O(n) 1, 5n log n + O(n) n log n + O(n) O(n log n) oder O(log n) mit linearer O(n log n) O(1) Suche O(1) klassisch Mergesort untere Schranke . – Seite 447/726 n log n − n kaum weniger O(n log n) O(n) n log n − 1, 443n n log n − 1, 443n Ω(n) Ω(1) . – Seite 448/726 4.7 Bucketsort Zunächst l = 1: Kann in Spezialfällen die n log n-Barriere durchbrochen werden? a1 , . . . , an ∈ {1, . . . , M } Klar, sortiere a1 , . . . , an ∈ {0, 1}. Aber wen interessiert das? – Array der Länge M , überall Zeiger auf zunächst leere Liste mit Zeiger auf Endelement (Liste = Eimer = Bucket). O(M ) – Wie ist es mit der lexikographischen Ordnung auf {0, 1}l ? – Durchlaufe Eingaben und hänge ai an | das {z Ende} von L(ai ). – Oder mit der lexikographischen Ordnung auf {1, . . . , M }l ? sichert Stabilität O(n) – Hänge L(1), . . . , L(M ) aneinander. Wörter der Länge l über einem Alphabet der Größe M Überweisungsformulare: l = 27, M ≈ 29 (26 Buchstaben, Komma, Bindestrich, Leerzeichen). O(M ) O(n + M ) . – Seite 449/726 . – Seite 450/726 M = 2927 → dann ist M ziemlich groß! Korrektheit Verallgemeinerter Bucketsort für l > 1 und Daten ai = (ail , . . . , ai1 ) ∈ {1, . . . , M }l . Sei ai < aj , d.h. ∃ k : aik < ajk und ∀ m > k : aim = ajm . – Für j = 1, . . . , l führe Bucketsort auf a1 , . . . , an durch, ordne in Runde j bezüglich der Buchstaben a1j , . . . , anj . → O(l · (n + M )). Eingabelänge n · l und typischerweise n > M . Dann ist die Rechenzeit O(n · l) linear in der Eingabelänge. . – Seite 451/726 In Runde k kommt ai in eine „frühere“ Liste als aj , steht also im Ergebnis vor aj . In den folgenden Runden bleibt diese Reihenfolge erhalten, da Bucketsort stabil ist. . – Seite 452/726 4.8 Das Auswahlproblem Was hilft? Randomisierung! Ziel: Finde das Datum x, das in der sortierten Folge an Rang k steht. Quickselect Runde 1 wie Quicksort mit Zerlegungsstrategie 3 (Es ist dann auch die Menge der Daten partitioniert in die Daten > x, = x, < x.) (Zerlegungsdatum von zufälliger Position i) Algorithmen mit worst case Zeit O(n) sind kompliziert und praktisch nicht sehr effizient (große Konstante). ai an Position r . – Seite 453/726 . – Seite 454/726 Rechenzeitanalyse für den average case (bei n verschiedenen Daten) r = k : Glück gehabt, ai ist das gesuchte Datum. → Rang des Zerlegungsdatum gleichverteilt auf {1, . . . , n}. r > k : Suche auf dem linken Teil (Größe r − 1) nach dem Datum mit Rang k . → Größe des zu lösenden Teilproblems r = 1: n−1 neuer Rang n−2 r = 2: .. . r < k : Suche auf dem rechten Teil (Größe n − r) nach dem Datum mit Rang k − r. Im Gegensatz zu Quicksort nur ein Teilproblem. r r r r .. . Korrektheit klar. = k − 1: n − (k − 1) 0 = k: = k + 1: k k+1 = k + 2: r = n: . – Seite 455/726 n−1 k−1 k−2 .. . 1 – k k .. . k . – Seite 456/726 V (n) := max{Vk (n)|1 ≤ k ≤ n}. Sei Vk (n) average case Anzahl an wesentlichen Vergleichen bei n Daten und Rang k . 1 Vk (n) = n − 1 + n Dann V (n) = Vkmax (n) (n) = . . . ( Vk−1 (n − 1) + Vk−2 (n − 2) + · · · + V1 (n − (k − 1)) + 0 + Vk (k) + Vk (k + 1) + · · · + Vk (n − 1)) Das sieht ja schlimm aus! 1 (V (n − 1) + V (n − 2) + · · · + V (n − k + 1) n + V (k) + V (k + 1) + · · · + V (n − 1)). ≤ n−1+ Wir kennen keine Methode zum Lösen dieser Gleichung. Rechnen unter Annahmen, Vermutung bilden, Vermutung beweisen. . – Seite 457/726 V (·) ist monoton wachsend. Dann maximaler Wert bei k = dn/2e. X X 1 → V (n) ≤ n − 1 + V (i) + V (i) n Vermutung 1: dn/2e≤i≤n−1 Vermutung 2: bn/2c+1≤i≤n−1 . – Seite 458/726 Behauptung: V (n) ≤ 4n. Induktionsbeweis: n = 1 : V (1) = 0 ≤ 4. 1, · · · , n − 1 → n : 4n ist monoton wachsend, also V (n) ≤ cn, aber welches c? Durchschnittliche Größe der Arrays in der 2. Runde: (3/4)n, dann (9/16)n, ... 9 1 3 n+ n + ··· = n = 4n. V (n) ≤ n + 4 16 1 − 3/4 . – Seite 459/726 . – Seite 460/726 X X 1 · 4i + 4i (nach Ind.vss.) n dn/2e≤i≤n−1 bn/2c+1≤i≤n−1 4 1 1 =n−1+ · · n · (n − 1) − · dn/2e · (dn/2e − 1) n 2 2 1 1 + · n · (n − 1) − · (bn/2c + 1) · bn/2c 2 2 4 1 1 1 1 2 2 2 =n−1+ n − n − dn/2e − bn/2c + dn/2e − bn/2c n 2 2 2 2 V (n) ≤ n − 1 + Vermutlich ist k = n/2 der worst case, Mediansuche. Aber dann suchen wir in Phase 2 nicht den Median! – In jeder Phase kann der Median gesucht sein, aber nicht in allen! → Unsere Analyse ist pessimistisch. Exaktes Ergebnis: 2 · (1 + ln 2) · n + o(n) ≈ 3, 39n. dn/2e2 + bn/2c2 ≥ (n/2)2 + (n/2)2 = n2 /2 4 1 2 2 ≤n−1+ n −n− n +1 n 4 4 3 ≤ n + · · n2 = 4n. n 4 . – Seite 461/726 Alle O(log n) Sortieralgorithmen in diesem Szenario sind sehr kompliziert und erst für sehr große n sinnvoll. 4.9 Sortieren auf Parallelrechnern Was hilft es, wenn wir viele Vergleiche gleichzeitig ausführen können? Praktisch effizienter Algorithmus: Batchersort. Damit mehrfacher Zugriff auf Daten zu einem Zeitpunkt ausgeschlossen ist, darf ai an maximal einem Vergleich zu einem Zeitpunkt beteiligt sein. Also höchstens n 2 . – Seite 462/726 Welcher unserer Sortieralgorithmen ist „parallelisierbar“? Mergesort. Vergleiche gleichzeitig. → Allgemeine Sortierverfahren brauchen mind. 2 log n − o(log n) Zeittakte. . – Seite 463/726 . – Seite 464/726 Batchersort BS(a1 , . . . , an ) S(n) für n = 2k P S(n) := Anzahl der Zeittakte für Batchersort. – n = 1: nichts zu tun – n > 1: := Anzahl der wesentliche Vergleiche für Batchersort. M (n) (b1 , . . . , bn/2 ) := BS(a1 , . . . , an/2 ) Gleichzeitig (c1 , . . . , cn/2 ) := BS(an/2+1 , . . . , an ) := Anzahl der wesentlichen Vergleiche für Batchermerge und zwei Folgen der Länge n. P M (n) := Anzahl der Zeittakte für Batchermerge und zwei Folgen der Länge n. S(1) = 0, P S(1) = 0. M (1) = 1, P M (1) = 1 (offensichtlich). (d1 , . . . , dn ) := BM(b1 , . . . , bn/2 ; c1 , . . . , cn/2 ). Batchermerge . – Seite 465/726 S(n) = 2 · S(n/2) + M (n/2) . – Seite 466/726 BM(a1 , . . . , an ; b1 , . . . , bn ) mit a1 ≤ · · · ≤ an und b1 ≤ · · · ≤ bn P S(n) = 1· P S(n/2) + P M (n/2) Anzahl möglicher Rangplätze für aj : n + 1, nämlich j, . . . , j + n. da die beiden rekursiven Aufrufe gleichzeitig stattfinden. Aber wie können wir das Reißverschlussverfahren für das Mischen zweier sortierter Folgen parallelisieren? Gar nicht, wir brauchen eine neue Idee. . – Seite 467/726 Wenn bk ≤ aj ≤ bk+2 bekannt ist, sind es nur noch 2 Rangplätze: j + k, j + k + 1. Falls aj ≤ b2 , sind es die Rangplätze: j, j + 1, falls aj ≤ b1 , ist es Rangplatz j , analog bn−1 ≤ aj und bn ≤ aj . . – Seite 468/726 BM(a1 , . . . , an ; b1 , . . . , bn ) n = 2k – n = 1: z1 = min(a1 , b1 ), z2 = max(a1 , b1 ), ein Vergleich – n > 1: (v1 , . . . , vn ) = BM(a1 , a3 , . . . , an−1 ; b1 , b3 , . . . , bn−1 ) Gleichzeitig Odd - Even - Merge (w1 , . . . , wn ) = BM(a2 , a4 , . . . , an ; b2 , b4 , . . . , bn ) ... v2 ↔ w 1 v3 ↔ w 2 Gleichzeitig ... vi+1 ↔ wi z 1 = v1 z2 = min(v2 , w1 ), z3 = max(v2 , w1 ) z4 = min(v3 , w2 ), z5 = max(v3 , w2 ) .. . z2i = min(vi+1 , wi ), z2i+1 = max(vi+1 , wi ) .. . z2n−2 = min(vn , wn−1 ), z2n−1 = max(vn , wn−1 ) z2n = wn . vn−1 ↔ wn−2 vn ↔ wn−1 . – Seite 469/726 . – Seite 470/726 → In der v -Folge sind i − 1 Daten kleiner als vi , davon j − 1 aus der a-Folge. Welche Rangplätze kann vi haben? – v1 ist das kleinere Element von a1 und b1 , also das kleinste aller Elemente. → In der v -Folge sind i − j b-Daten kleiner als vi , also b1 , b3 , . . . , b2(i−j)−1 . – vi , i ≥ 2: → vi = a2j−1 oder vi = b2j−1 , o. B. d. A. vi = a2j−1 . → 2j − 2 a-Daten kleiner als vi und n − 2j + 1 größer. → Von den 2j − 2 a-Daten kleiner als vi sind j − 1 in der v -Folge. . – Seite 471/726 → Mindestens 2i − 2j − 1 b-Daten sind kleiner als vi und 2j − 2 a-Daten sind kleiner als vi . → Rang(vi ) ≥ 2i − 2. . – Seite 472/726 → Von den n − 2j + 1 a-Daten größer als vi sind n/2 − j in der v -Folge. → In der v -Folge sind n − i Daten größer als vi , davon n/2 − j aus der a-Folge. Also 2i − 2 ≤ Rang(vi ) ≤ 2i − 1. Analog 2i − 2 ≤ Rang(wi−1 ) ≤ 2i − 1. → z2i−2 = min(vi , wi−1 ) und z2i−1 = max(vi , wi−1 ). → In der v -Folge sind n/2 − i + j b-Daten größer als vi , also b2i−2j−1 , . . . , bn−1 . → Batchermerge arbeitet korrekt. → Mindestens n − 2i + 2j b-Daten sind größer als vi und n − 2j + 1 a-Daten sind größer als vi , insgesamt 2n − 2i + 1 Daten. → Rang(vi ) ≤ 2i − 1. . – Seite 473/726 M (n) = 2 · M (n/2) + n − 1 und M (1) = 1. Zur Erinnerung: S(1) = 0 und S(n) = 2 · S(n/2) + M (n/2) = 2 · S(n/2) + → M (n) = n log n + 1 (siehe Analyse von Algo. 1.3.3), PM (n) = 1· PM (n/2)+ 1 und PM (1) . – Seite 474/726 =1 n 2 log n2 + 1 → S(n) = 14 n · log n · (log n − 1) + n − 1 (lässt sich mit Induktionsbeweis verifizieren). gleichzeitige Ausführung der Mischvorgänge und auch der letzten n − 1 Vergleiche. P S(1) = 0 und P S(n) = P S(n/2) + P M (n/2) = P S(n/2) + log n = log n + log(n/2) + log(n/4) + · · · + log(n/n) = log n + (log n − 1) + (log n − 2) + · · · + 0 = 12 · log n · (log n + 1). → PM (n) = log n + 1. . – Seite 475/726 . – Seite 476/726 Das Batcher-Sortiernetzwerk für n = 16 a1 b1 v1 z1 Hardwaremäßige Realisierung – Sortiernetzwerke a2 b2 w1 z2 a3 b3 v2 z3 a1 a2 a3 a4 a5 a6 a7 a8 a4 b4 w2 z4 a5 b5 v3 z5 a6 b6 w3 z6 a7 b7 v4 z7 a8 b8 w4 z8 a9 c1 v5 z9 a10 c2 w5 z 10 a11 c3 v6 z 11 a12 c4 w6 z 12 a13 c5 v7 z 13 a14 c6 w7 z 14 a15 c7 v8 z 15 a16 c8 w8 z 16 ai bei „Prozessor Pi “ Kante zwischen Pi und Pj , i < j : Prozessoren Pi und Pj vergleichen ihre Daten, Pi erhält das kleinere Datum, Pj das größere Datum, usw. Vergleich von max(a2 , a5 ) mit min(a6 , a8 ). T1 T2 T3 T4 T5 T6 T7 T8 T9 T10 T11 Pi kommuniziert nur mit Pi+2k und Pi−2k ! . – Seite 477/726 . – Seite 478/726 5 Entwurfsmethoden für Algorithmen 5.1 Vorbemerkungen Aber: – Methoden sind nicht präzise definiert. – Es gibt kein Patentrezept. – Methoden sind nicht exakt abgrenzbar. – Entwurfsmethoden genügen für Alltagsprobleme. Daher: – In schwierigen Fällen oft Kombination von neuen Ideen mit gängigen Entwurfsmethoden. – Allgemeine, aber nicht formal eindeutige Beschreibung der Methoden. – Kenntnis allgemeiner Entwurfsmethoden unabdingbar. – Exemplarische Anwendung der Methoden auf ausgewählte Probleme. – Analyse der Algorithmen, Korrektheit, Rechenzeit. . – Seite 479/726 . – Seite 480/726 Branch-and-Bound Algorithmen Greedy Algorithmen KP. Geldwechselproblem, Traveling Salesperson Problem (TSP), Rucksackproblem (KP), Bin Packing Problem (BPP), minimale Spannbäume (MSTP). Divide-and-Conquer Algorithmen Schon bekannt: Mergesort, Quicksort, Maxsummenproblem, Batchersort. Eine allgemeine Analysemethode. Matrixmultiplikation, schnelle Fouriertransformation (FFT), Multiplikation von Polynomen, nächste Nachbarn in der Ebene. Dynamische Programmierung Optimale statische Suchbäume, KP, All Pairs Shortest Paths (APSP), Sequence Alignments. Hybride Algorithmen Single Source Shortest Paths (SSSP). Greedy und dynamische Programmierung. . – Seite 481/726 Sweepline Technik . – Seite 482/726 5.2 Greedy Algorithmen Rechteckmaßproblem. Voraussetzungen: α–β –Pruning – Optimierungsproblem. Analyse von Spielbäumen. – Lösungen bestehen aus Einzelstücken (Kanten eines Baumes, Bits eines Vektors, Funktionswerte einer Funktion, z. B. einer Permutation, Entscheidungen über einzelne Objekte). Randomisierte Suchheuristiken Randomisierte lokale Suche (RLS), Metropolis Algorithmus (MA), Simulated Annealing (SA), evolutionäre Algorithmen (EA), genetische Algorithmen (GA). – Teillösungen können qualitativ unterschieden werden. – Wähle das nächste Teil einer Teillösung, so dass der Wert maximal gesteigert wird. . – Seite 483/726 . – Seite 484/726 Und das soll optimale Lösungen ergeben? Geldwechselproblem Beispiel: Zeitplanung und das Bearbeiten von Übungsaufgaben. Gegeben: Eine Währung durch die Wertigkeit von Scheinen und Münzen: nk > nk−1 > · · · > n2 > n1 = 1. Eingabe: N , der zu realisierende Betrag. Ziel: Möglichst wenige Scheine und Münzen. – Probleme, bei denen greedy Algorithmen stets optimale Lösungen liefern. – Probleme, bei denen eine maximale (multiplikative) Abweichung vom optimalen Lösungswert garantiert ist. – Probleme, bei denen greedy Lösungen sehr schlecht sein können. Minimiere ak + ak−1 + · · · + a1 (ai Anzahl der ni -Münzen) unter den Nebenbedingungen ak · nk + ak−1 · nk−1 + · · · + a1 · n1 = N ak , . . . , a 1 ≥ 0 ak , . . . , a1 ganzzahlig. . – Seite 485/726 Teillösungen: Eine Auswahl an Münzen mit Gesamtwert ≤ N . . – Seite 486/726 Wie gut ist die Lösung? Optimal für Euro, Dollar, . . . (Übungsaufgabe), Wert der Teillösung: Erreichter Betrag pro Münze. aber Greedy Strategie: Füge größtmögliche Münze hinzu, ohne den Betrag N zu überschreiten. n3 = 2n2 + 1, n1 = 1 N = 3n2 Effiziente Implementierung: Für n3 ≥ 3 sind 3 Münzen optimal (n2 , n2 , n2 ). Greedy Lösung R := N (Restbetrag) für i = k, . . . , 1 : ai := bR/ni c (max. erlaubte Münzzahl) R := R − ai ni (neuer Restbetrag) O(k). (z. B. n3 = 11, n2 = 5, n1 = 1) (z. B. N = 15) n3 = 1, R = n2 − 1, n2 = 0, R = n2 − 1, n1 = n2 − 1, R = 0, also n2 Münzen. Beliebig schlecht. Da n1 = 1, ist am Ende R = 0 und wir erhalten eine Lösung. . – Seite 487/726 . – Seite 488/726 Traveling Salesperson Problem (TSP) Teillösungen: Eingabe: Kostenmatrix C = (c(i, j)), c(i, j) Kosten, um von i nach j zu kommen. Mögliche Lösungen: Rundreisen oder Touren, die jeden Ort genau einmal besuchen. π(1), . . . , π(i), Anfangsstück einer Rundreise. Wert der Teillösung: Kosten des Anfangsstückes. Greedy Strategie: Ziel: Eine billigste Rundreise. Minimiere c(π(1), π(2)) + c(π(2), π(3)) + · · · + c(π(n − 1), π(n)) + c(π(n), π(1)), wobei π : {1, . . . , n} → {1, . . . , n} bijektiv (Permutation), c(π(1), π(2)) + · · · + c(π(i − 1), π(i)), Wähle π(i + 1) ∈ {1, . . . , n} − {π(1), . . . , π(i)} („neuer Ort“) mit c(π(i), π(i + 1)) minimal. O(n), insgesamt O(n2 ). Am Ende π(1), . . . , π(n) paarweise verschieden → π beschreibt Rundreise. π(i): i-ter Ort auf der Rundreise. O. B. d. A.: π(1) = 1. . – Seite 489/726 . – Seite 490/726 Rucksackproblem, Knapsack Problem (KP) Wie gut ist die Lösung? c(i, i + 1) = 1 c(n, 1) =M c(i, j) =2 Eingabe: ,1≤i≤n−1 sonst. Gewichte g1 , . . . , gn ∈ N für n Objekte 1, . . . , n, Nutzenwerte v1 , . . . , vn ∈ N, Gewichtschranke G. Mögliche Lösungen: Optimale Rundreise 1, 2, 3, . . . , n − 2, n, n − 1, 1 Kosten (n − 3) · 1 + 3 · 2 = n + 3. Greedy Rundreise 1, 2, 3, . . . , n − 2, n − 1, n, 1 Kosten (n − 1) · 1 + 1 · M = M + n − 1. Ziel: Beliebig schlecht. (Das ähnelt dem Beispiel mit den Übungsaufgaben und dem „dicken Ende“.) . – Seite 491/726 Teilmengen der Objekte mit Gesamtgewicht höchstens G. Auswahl von Teilmengen mit maximalem Nutzen. λ = (λ1 , . . . , λn ) ∈ {0, 1}n beschreibt die Auswahl von I = {i|λi = 1}. Maximiere λ 1 v1 + · · · + λ n vn unter den Nebenbedingungen λ1 g 1 + · · · + λ n g n ≤ G und λ1 , . . . , λn ∈ {0, 1}. . – Seite 492/726 Teillösungen: λ1 , . . . , λn ∈ {0, 1, ∗} mit λi = ∗ heißt: „noch P keine Entscheidung über Objekt i“, wobei g i ≤ G. i|λi =1 P Wert der Teillösung: vi . i|λi =1 P Greedy Strategie: Wähle j mit λj = ∗, so dass gi + g j ≤ G i|λi =1 und vj maximal, setze λj = 1. Das ist arg dumm, da Gewicht und Nutzen nicht in Relation gesetzt werden. Analog: Wähle j mit λj = ∗ und gj minimal. Setze λj = 1, falls dann Gewichtsgrenze nicht überschritten. Effektivität: ei := vi /gi betrachte Nutzen-Kosten-Verhältnis. Sortiere die Effektivitätswerte, nummeriere Objekte neu, so dass e1 ≥ e2 ≥ · · · ≥ en . O(n log n). Greedy Strategie: Für i = 1, . . . , n: Falls gi ≤ G, setze λi := 1 und G := G − gi . sonst setze λi := 0. O(n). Die Lösungen erfüllen stets die Nebenbedingungen. . – Seite 493/726 Wie gut ist die Lösung? n = 2: g1 = 1, v1 = 1 ⇒ e1 = 1. g2 = G, v2 = G − 1 ⇒ e2 = 1 − Relaxiertes Optimierungsproblem Statt Nebenbedingungen λ1 , . . . , λn ∈ {0, 1} nun λ1 , . . . , λn ∈ [0, 1]. 1 G. Die Nebenbedingung wurde abgeschwächt (relaxiert). Optimale Bepackung: λ1 = 0, λ2 = 1. Nutzen G − 1. Greedy Lösung: . – Seite 494/726 Interpretation: Objekte beliebig teilbar, λi -Anteil von Objekt i hat Gewicht λi gi und Nutzen λi vi . λ1 = 1, λ2 = 0. Nutzen 1. Beliebig schlecht. TSP und KP sind NP-äquivalente Optimierungsprobleme! → GTI . – Seite 495/726 Wozu ist das gut? → Branch-and-Bound Methoden in Kap. 5.5 . – Seite 496/726 Sei e1 ≥ · · · ≥ en . Die Lösung ist optimal. Greedy-Strategie: Berechne maximales i mit g1 + · · · + gi ≤ G und setze λ1 := 1, . . . , λi := 1. G := G − g1 − g2 − · · · − gi (Noch verfügbares Gewicht) Falls i < n, setze λi+1 := G/gi+1 λi+2 := 0, . . . , λn := 0. O(n). Da g1 + · · · + gi+1 > G, ist gi+1 > Restkapazität. 1. Fall i = n: Alle Objekte können eingepackt werden und werden eingepackt → optimal. 2. Fall i < n: Es wird das Gewichtslimit genau ausgeschöpft, da λi+1 gi+1 = G = Restkapazität. Teile gedanklich Objekt i in gi Einzelobjekte mit Gewicht 1 und Nutzen vi /gi , also hat jedes Teil Effektivität ei . Also ist die Lösung zulässig. . – Seite 497/726 Wir erhalten g1 + · · · + gn Objekte mit Gewicht 1 und sollen daraus Objekte mit Gesamtgewicht G und maximalem Nutzen wählen. Offensichtlich: Wähle G Objekte mit größtem Nutzen (entspricht der alten Effektivität). . – Seite 498/726 Bisher Greedy Strategien optimal (Geldwechselproblem bei Euro-Währung, relaxiertes KP) oder beliebig schlecht (allgemeines Geldwechselproblem, TSP, KP) nun ein Beispiel: Greedy Strategie nicht immer optimal, aber nicht beliebig schlecht. Genauer: Minimierungsproblem und Wert der Greedy Lösung ist durch eine Konstante beschränkt. Wert optimaler Lösung Weiteres Teilen der Objekte bringt keine Verbesserung. . – Seite 499/726 . – Seite 500/726 Teillösungen: Verpackung der Objekte 1, . . . , j . Bin Packing Problem (BPP) Eingabe: Wert der Teillösung: Anzahl benutzter Kisten. Gewichte g1 , . . . , gn ∈ N für n Objekte mit gi ≤ G. Mögliche Lösungen: f : {1, . . . , n} → {1, . . . , n}, d. h. Objekt j kommt in Kiste f (j), wobei Gesamtgewicht pro Kiste höchstens G. Ziel: Greedy Strategie 1 (First Fit FF) Für j = 1, . . . , n: Finde kleinstes i mit P 1≤k≤j−1|f (k)=i Setze f (j) := i. O(n2 ). Minimierung der Anzahl der Kisten, in die Objekte gelegt werden. Minimiere i unter den NebenbedingungenP ∃f : {1, . . . , n} → {1, . . . , i} : j:f (j)=k g k + g j ≤ G. gj ≤ G für 1 ≤ k ≤ i. . – Seite 501/726 . – Seite 502/726 Für jede Problemeingabe (instance) I gilt: F F (I) BF (I) OP T (I) ≤ 2 und OP T (I) ≤ 2. Greedy Strategie 2 (Best Fit BF) Für j = 1, . . . , n: P Finde i mit gk + g j ≤ G 1≤k≤j−1|f (k)=i P und gk maximal. Für beide Strategien gilt: Benutzen sie zwei Kisten, ist deren Gesamtinhalt mindestens G + 1. (Ansonsten andere Objektverteilung.) Sei g Gewicht der benutzten Kiste mit kleinster Beladung. 1≤k≤j−1|f (k)=i Vollste Kiste, in die Objekt j noch passt. O(n2 ) → O(n log n). 1. Fall: g ≥ G/2 ⇒ alle Kisten zu mindestens 50% genutzt. ⇒ selbst bei beliebiger Teilbarkeit der Objekte mindestens halb so viele Kisten nötig. . – Seite 503/726 . – Seite 504/726 2. Fall: g < G/2 und es wird nur eine Kiste benutzt ⇒ Lösung optimal F F (I) 17 BF (I) 17 Auch beweisbar: OP T (I) ≤ 10 , OP T (I) ≤ 10 . Andererseits: ∃I (sogar mit beliebig großem OP T (I)-Wert. F F (I) 5 BF (I) 5 OP T (I) ≥ 3 , OP T (I) ≥ 3 . 3. Fall: g < G/2 und es werden k ≥ 2 Kisten benutzt. ⇒ Gesamtbeladung ≥ g + (k − 1) · (G − g + 1) = G + 1 + (k − 2) · (G − g + 1) ≥ G + 1 + (k − 2) · G/2, da k ≥ 2 und g < G/2 > k · G/2 n = 18m g1 = · · · = g6m = 19 g6m+1 = · · · = g12m = 43 g12m+1 = · · · = g18m = 64 G = 126 ⇒ durchschnittliche Belastung der Kisten mehr als 50%. ⇒ mindestens halb so viele Kisten nötig. . – Seite 505/726 . – Seite 506/726 OP T (I) = 6m. 19 + 43 + 64 = 126, 6m Kisten voll ausgelastet. Hätten die Objekte in anderer Reihenfolge vorgelegen, dann hätte die Strategie optimale Lösungen berechnet. F F (I) = BF (I) = 10m. Zunächst m Kisten mit je 6 Objekten der Größe 19, 6 · 19 = 114, Restkapazität 12. Dann 3m Kisten mit je 2 Objekten der Größe 43, 2 · 43 = 86, Restkapazität 40. Dann 6m Kisten mit je 1 Objekt der Größe 64, 1 · 64 = 64, Restkapazität 62. FFD (FF decreasing) Sortiere Objekte nach fallender Größe und nummeriere neu. Wende FF auf g1 ≥ g2 ≥ · · · ≥ gn an. Analog BFD. 11 ∀I : F F D(I) ≤ 11 9 OP T (I) + 4, BF D(I) ≤ 9 OP T (I) + 4. ∀m∃I : OP T (I) ≥ m und F F D(I) = BF D(I) = 11 9 OP T (I). Worst case gleich, aber in Anwendungen: BF besser als FF und BFD besser als FFD. . – Seite 507/726 . – Seite 508/726 Minimale aufspannende Bäume, minimale Spannbäume, Minimum Spanning Tree Problem (MSTP) Greedy Strategie: Wähle billigste Kante, die keinen Kreis schließt. Effiziente Implementierung: (Algorithmus von Kruskal) Sortiere die Kanten nach aufsteigenden Kosten, nach Umnummerierung c(e1 ) ≤ · · · ≤ c(em ). Verwende UNION-FIND- Datenstruktur für die Zusammenhangskomponenten bzgl. der gewählten Kanten. Initialisierung: Menge i enthält Knoten i. Ein ungerichteter zusammenhängender Graph G = (V, E) mit ganzzahligen, positiven Kantenkosten c(e). Eingabe: Mögliche Lösungen: Ziel: Bäume mit Knotenmenge V . ein billigster Baum. Teillösungen: Kreisfreie Kantenmengen. Wert der Teillösung: Summe der Kosten der gewählten Kanten. . – Seite 509/726 Für i = 1, . . . , m : sei ei = (vi , wi ), A := FIND(vi ), B := FIND(wi ), falls A 6= B , wähle ei , UNION(A, B). Vorzeitiges Stoppen: Bäume enthalten n − 1 Kanten, stoppe, wenn n − 1 Kanten gewählt sind. . – Seite 510/726 Verwende Heap-Datenstruktur. Heap Creation Phase für alle m Kanten und Min-Heap. O(m) An der Wurzel billigste Kante, entfernen, letzte Kante nach oben, reheap. O(log m) pro Kante Wenn m∗ Kanten betrachtet werden, Kosten Lohnt es dann noch, alle Kanten und Kosten zu sortieren? . – Seite 511/726 – bei Arrays und Listen für UNION-FIND: O(m + m∗ log m + m∗ + n log n). – bei wurzelgerichteten Bäumen und Pfadkomprimierung für UNION-FIND: O(m + m∗ log m + (n + m∗ ) log∗ n). . – Seite 512/726 Es wird sicher ein Baum auf V konstruiert. Erhalten wir minimale Spannbäume? B 10 A 16 13 12 13 G 12 D 20 Kantenreihenfolge 11 C 11 12 13 F 18 15 E 12 16 17 15 14 H 12 I 10(A, B) 11(B, C), (C, D) 12(B, D), (B, G), (C, E), (E, F ), (H, I) 13(A, F ), (A, G), (D; E) 14(H, F ) 15(E, I), (G, I) 16(A, D), (F, I) 17(G, H) 18(G, F ) 20(D, F ) Beh.: Für alle i = 0, . . . , m gibt es einen minimalen Spannbaum, der die gleiche Teilmenge aus {e1 , . . . , ei } an Kanten enthält wie der im Algorithmus von Kruskal konstruierte Spannbaum. Für i = m heißt das: Algo. von Kruskal liefert minimale Spannbäume. Induktionsbeweis: i = 0: Offensichtlich, noch keine Entscheidung getroffen. . – Seite 513/726 . – Seite 514/726 2. Fall: ei verbindet die Zusammenhangskompnenten Z und Z 0 von Gi−1 . i − 1 → i: Sei Gi−1 der Graph aus den vom Algo. von Kruskal aus {e1 , . . . , ei−1 } gewählten Kanten. Indvss.: ∃ minimaler Spannbaum T , der aus {e1 , . . . , ei−1 } dieselben Kanten wählt. Algo. von Kruskal wählt ei . Falls T auch ei enthält → Ind.beh. Falls T die Kante ei nicht enthält, füge sie hinzu. Z 1. Fall: ei verbindet zwei Knoten derselben Zusammenhangskomponente von Gi ⇒ Algo. von Kruskal wählt ei nicht, T kann ei wegen Kreisfreiheit nicht enthalten ⇒ Ind.beh. ei Z0 ej mit j > i Es entsteht ein Kreis. Mindestens eine Kante ej auf dem Kreis, die nicht in Gi−1 enthalten ist und nicht ei ist. Zusätzliche Kanten T . – Seite 515/726 . – Seite 516/726 Also ist j > i und c(ej ) ≥ c(ei ). Breche Kreis auf, indem ej entfernt wird. Es entsteht ein Baum T 0 mit c(T 0 ) ≤ c(T ). Da T minimal, ist c(T 0 ) = c(T ) und T 0 minimal. T 0 stimmt mit T und damit mit Algo. von Kruskal in der Auswahl der Kanten aus {e1 , . . . , ei−1 } überein. Algo. von Kruskal wählt ei und T 0 enthält ei → Ind.beh. 5.3 Dynamische Programmierung Voraussetzungen: – Optimierungsproblem (hier stets), aber auch Konstruktionsproblem (GTI). – Problem kann auf noch nicht bekannte Weise in Teilprobleme vom selben Typ zerlegt werden. – Erst nach der Lösung der Teilprobleme lässt sich die optimale (passende) Zerlegung berechnen. – Lösung besteht aus optimaler Zerlegung und optimalen Lösungen der Teilprobleme. . – Seite 517/726 Es werden stets optimale Lösungen berechnet. . – Seite 518/726 Rekursion kann gefährlich sein! Kanonische Implementierung – Teilprobleme werden sehr oft, eventuell exponentiell oft gelöst (→ Fibonacci-Zahlen). – Betrachte alle Zerlegungen des Anfangsproblems. – Löse rekursiv die enstehenden Teilprobleme. Grundidee der dynamischen Programmierung: – „Ganz kleine“ Probleme sind direkt lösbar. – Füge Lösungen der Teilprobleme zu Gesamtlösungen für jede Zerlegung zusammen. – Finde beste Zerlegung und damit Gesamtlösung. . – Seite 519/726 – Löse Teilprobleme systematisch nach „aufsteigender Größe“, speichere die Lösungen in einer Tabelle (meist speicherplatzintensiv) und greife bei Bedarf auf die gespeicherten Lösungen zu. . – Seite 520/726 Bellmansches Optimalitätsprinzip Sie muss jeweils aufgestellt werden und ihre Korrektheit muss bewiesen werden. Bei zerlegbaren Problemen müssen die gebildeten Teilprobleme optimal gelöst werden. Bellman hatte nur Optimierungsprobleme im Sinn, daher Optimalitätsprinzip und Optimalitätsgleichung. Dies ist ein „Prinzip“ und kein „Theorem“. Diese Begriffe werden heute auch bei Konstruktionsproblemen verwendet, auch wenn dabei von Optimierung keine Rede sein kann. ∧ Es gibt nur: Konstruktion möglich ( = optimal) oder nicht ∧ möglich ( = nicht optimal). Ob Probleme dem bellmanschen Optimalitätsprinzip genügen, muss für jedes Problem neu überprüft werden! Die Beziehung zwischen optimalem Wert der Gesamtlösung, den Zerlegungsmöglichkeiten und den optimalen Werten der Teillösungen heißt bellmansche Optimalitätsgleichung. . – Seite 521/726 Typisches Beispiel: Probleme vom Intervalltyp . – Seite 522/726 Optimale statische binäre Suchbäume – Grundbereich [1, n], Statisch: nur SEARCH. Schlüssel: S1 < S2 < · · · < Sn . Zugriffswahrscheinlichkeiten: pi für Si , q0 für (−∞, S1 ), qj für (Sj , Sj+1 ) und qn für (Sn , ∞). – P (i, j): Teilproblem für Bereich [i, j], 1 ≤ i ≤ j ≤ n. – P (i, i) direkt lösbar, gesucht Lösung von P (1, n). – W (i, j): Wert der Lösung von P (i, j) S2 k – Zerlegungspunkt i ≤ k ≤ j min W (i, j) = V (i, j)+ max {W (i, oder oder | so ähnlich O(n2 ) Probleme (i, k) (k + 1, j) oder S1 (i, k − 1) S4 k−1 k ) + W (k, j)|i ≤ k ≤ j} {z O(j−i+1)=O(n) (−∞, S1 ) } (S1 , S2 ) S3 (S4 , ∞) li := # Knoten auf dem Weg von Wurzel zu Si . mj := # Knoten auf Weg von Wurzel zum nil-Zeiger für j -ten Zwischenraum. (S2 , S3 ) O(n3 ) . – Seite 523/726 (S3 , S4 ) . – Seite 524/726 Teilproblem P (i, j) Erwartete (durchschnittliche) Zugriffszeit: E(T ) = X 1≤i≤n pi l i + X – Schlüssel Si , . . . , Sj mit Gewichten pi , . . . , pj . qj m j . – Zwischenräume (·, Si ), (Si , Si+1 ), . . . , (Sj , ·) mit Gewichten qi−1 , . . . , qj . 0≤j≤n Finde binären Suchbaum mit minimalem E(T )-Wert. – Gesamtgewicht p(i, j) = pi + · · · + pj + qi−1 + · · · + qj . Übergang zu Teilproblemen → bedingte Wahrscheinlichkeiten. – Auch P (i, i − 1): Nur Zwischenraum (Si−1 , Si ). – Bäume enthalten Wurzel Sk . Ausweg: pi und qj beliebige Gewichte, nicht notwendig Wahrscheinlichkeiten. X X C(T ) = pi l i + qj m j . 1≤i≤n – Die Wurzel zerlegt den Baum in Teilbäume. 0≤j≤n . – Seite 525/726 [i, j] . – Seite 526/726 [i, j] Sk Sk T Wie hängen die Kosten von T , T1 und T2 zusammen? [i, k − 1] [k + 1, j] Falls Wurzel Sk , wird Baum optimal, wenn beide Teilbäume für ihren Bereich optimal sind, also gilt hier das bellmansche Optimalitätsprinzip. T1 T2 Für alle Ziele in T1 sind die Kosten in T1 um 1 kleiner als in T , für T2 analog. C(T ) = p(i, j) + C(T1 ) + C(T2 ) Kosten 1 für alle Möglichkeiten in T Spezialfall: T1 nur nil-Zeiger: C(T1 ) = 0, analog für T2 . . – Seite 527/726 . – Seite 528/726 Tabelle die gesuchte Lösung Wir müssen alle Möglichkeiten für die Wurzel betrachten, können uns aber auf optimale Lösungen der Teilprobleme beschränken. 1 2 3 4 5 6 7 8 9 10 11 i 1 2 3 4 5 6 7 8 9 10 11 C(i, j) := Kosten optimaler Baum für P (i, j). C(i, i − 1) = 0. C(i, i) = p(i, i) (es gibt nur einen Baum). j > i: C(i, j) = p(i, j) + min{C(i, k − 1) + C(k + 1, j)|i ≤ k ≤ j}. Berechne die Probleme nach wachsendem l = j − i: für l = 0, . . . , n − 1 Platz O(n2 ) für i = 1, . . . , n − l Zeit O(n3 ) berechne C(i, i + l). bellmansche Optimalitätsgleichung. . – Seite 529/726 . – Seite 530/726 Aber wir wollen nicht nur C(1, n), sondern auch einen optimalen Baum T (1, n)! Und was ist an der Rekursion so schlimm? R(n): Rechenzeit für Grundbereich der Länge n. R(2) ≥ 1 P R(n) ≥ 1≤i≤n−1 (R(i) + R(n − i)) Speichere in der Tabelle nicht nur die C(i, j)-Werte, sondern auch den Schlüsselindex k(i, j), für den die bellmansche Optimalitätsgleichung den kleinsten Wert annimmt. Top-Down-Konstruktion Teilprobleme bei Wurzel Si n−2 Sk(1,n) n−2 ≥ 2 · R(n − 1) ≥ 4 · R(n − 2) ≥ 2 R(2) ≥ 2 . Und das ist schlimm! Wir können die Bereiche effizient berechnen und optimale Wurzeln durch table-look-up erhalten. Sk(1,k(1,n)−1) Sk(k(1,n)+1,n) usw. Extraaufwand nur O(n). . – Seite 531/726 . – Seite 532/726 Aha: Rucksackproblem (KP) – Vieles ist viel einfacher als bei den statischen binären Suchbäumen, aber – es gibt nicht auf kanonische Weise Teilprobleme. – Diese müssen erst „erdacht“ werden. 1, wenn es in R(k, g) optimal ist, Objekt k einzupacken 0, sonst Objekt n nicht einpacken einpacken KP mit Objekten 1, . . . , n − 1, KP mit Objekten 1, . . . , n − 1, bisheriger Nutzen 0. bisheriger Nutzen vn . Gewichtsschranke G, R(k, g): Rucksackproblem mit Objekten 1, . . . , k und Gewichtsschranke g , wobei 1 ≤ k ≤ n, 0 ≤ g ≤ G. F (k, g): Wert einer optimalen Lösung von R(k, g). D(k, ( g) := Gewichtsschranke G − gn , Kleine und verbotene Teilprobleme: F (0, g) := 0, D(0, g) := undef. F (k, 0) := 0, D(k, 0) := 0. F (k, g) := −∞ für g < 0. . – Seite 533/726 Tabelle ...... 1 1 Bellmansches Optimalitätsprinzip: ...... Wenn wir uns entschieden haben, ob wir Objekt n einpacken, muss das entstehende Teilproblem optimal gelöst werden. Bellmansche Optimalitätsgleichung: I ...... G (F (k, g), D(k, g)) II O(1). . – Seite 535/726 ...... einpacken F (k, g) = max{F (k − 1, g), F (k − 1, g − gk ) + vk } | {z } | {z } D(k, g) = 1, falls II ≤ I , sonst D(k, g) = 0. g k bereits erzielter Nutzen nicht einpacken . – Seite 534/726 Lösungswert n Tabelle wird zeilenweise von oben nach unten gefüllt. O(nG). . – Seite 536/726 F (n, G) = Wert einer optimalen Lösung. Was ist O(nG) für eine „komische“ Rechenzeit? Polynomiell? Nein, z. B. alle gi , vi , G ≤ 2n → Bitlänge O(n2 ), aber G = 2n → Rechenzeit O(n2n ) exponentiell. Aber: Falls alle G ≤ p(n) für ein Polynom p, dann Rechenzeit O(n · p(n)) polynomiell. Optimale Lösung 1 n einpacken, D(n − 1, G − gn ) 1 ... 0 1 ... 0 n nicht einpacken, D(n − 1, G) 0 Nur ein Weg wird betrachtet. O(n). D(n, G) Rechenzeiten heißen pseudopolynomiell, wenn sie polynomiell sind, falls die Größe der Zahlen polynomiell beschränkt wird. . – Seite 537/726 All Pairs Shortest Paths (APSP) Eingabe: Kostenmatrix C = (c(i, j))1≤i,j≤n , c(i, j) := Kosten des direkten Weges von i nach j , c(i, j) ≥ 0, c(i, i) = 0. Ziel: ∀1 ≤ i, j ≤ n: Billigste Wege von i nach j . Wieder brauchen wir eine neue Idee für Teilprobleme! Problem Pk : Alle Zwischenorte stammen aus {1, . . . , k}. Unser eigentliches Problem: Pn . Anfangsproblem P0 ist trivial, da kein Zwischenort erlaubt ist. D = (d(i, j)) : d(i, j) := Distanz von i nach j = Kosten des billigsten Weges von i nach j . dk (i, j) und Nk (i, j) die Lösungen zu Pk . d0 (i, j) = c(i, j), N0 (i, j) = j . Die Beschreibung des ganzen Weges kann Ω(n) Knoten beinhalten, daher N (i, j) direkter Nachfolger auf billigstem Weg von i nach j . → Ergebnis auf Platz . – Seite 538/726 Da c(i, j) ≥ 0, gibt es billigste, kreisfreie Wege. nicht besuchen Also: Zwischenort k O(n2 ). einmal besuchen Wegkonstruktion i, N (i, j), N (N (i, j), j) , . . . . – Seite 539/726 . – Seite 540/726 Bellmansches Optimalitätsprinzip: Soll k Zwischenort sein, brauchen wir billigste Wege i → k und k → j . Diese Wege enthalten k nicht als Zwischenort. Bellmansche Optimalitätsgleichung: Knoten k nicht besuchen Knoten k besuchen Nk (i, j) = ( Nk−1 (i, j), Nk−1 (i, k), Wenn (Nk , dk ) berechnet sind, kann (Nk−1 , dk−1 ) überschrieben werden. Die Werte werden für k = 0, . . . , n berechnet. dk (i, j) = min {dk−1 (i, j), dk−1 (i, k) + dk−1 (k, j)} I Der Platz kann auf O(n2 ) beschränkt werden. Die einzelnen Matrizen können in beliebiger Reihenfolge (kanonisch: zeilenweise) berechnet werden. II falls I ≤ II O(1) für jedes (k, i, j) ↓ sonst. O(n3 ). . – Seite 541/726 . – Seite 542/726 Minimale Kosten, um y aus x zu konstruieren. Globales Alignment zweier Sequenzen Erlaubte Operationen: – Verallgemeinerung des Maxsummenproblems aus Kap. 1 auf ein Basisproblem der Molekularbiologie. – Wandle xi in yj um, Kosten c(xi , yj ). Dabei ist c(xi , yj ) = c(yj , xi ). – Ähnlichkeit (Alignment = Anpassung) zweier Sequenzen x, y ∈ Σ∗ , Σ endliches Alphabet, z. B. – Eliminiere xi , d. h. ersetze xi durch Lücke „–“, Kosten d ≥ 0. Σ = {Adenin, Guanin, Cytosin, Thymin} für DNA oder Σ enthält die 20 Aminosäuren. – Füge yj in eine Lücke (Zwischenraum) von x ein, Kosten d ≥ 0. – x = (x1 , . . . , xn ), y = (y1 , . . . , ym ) (n 6= m möglich). Also c(xi , −) = c(−, yj ) = d. Wie messen wir die Ähnlichkeit? . – Seite 543/726 . – Seite 544/726 Mögliche Lösungen: Beispiel: A C G – – G C T – A T A C ← x∗ A – G T T G – T C – C G C ← y∗ ↓ ↓ ↓ Kosten d Kosten c(G, G) Kosten c(T, C) c(x∗ , y ∗ ) = X x∗ Erweiterung von x um Lücken, y ∗ Erweiterung von y um Lücken, Länge(x∗ ) = Länge(y ∗ ), ∀i : (x∗i , yi∗ ) 6= (−, −). → Länge(x∗ ) ≤ n + m. Lösungswert: c(x∗ , y ∗ ). Ziel: c(x∗i , yi∗ ). Finde Alignment (x∗ , y ∗ ) mit kleinstem c-Wert. Die Teilprobleme ergeben sich fast kanonisch. P (i, j) : Alignment von (x1 , . . . , xi ) und (y1 , . . . , yj ), 0 ≤ i ≤ n, 0 ≤ j ≤ m . C(i, j) : Lösungswert für P (i, j). A(i, j) : Letzte Position eines optimalen Alignment von (x1 , . . . , xi ) und (y1 , . . . , yj ). 1≤i≤k . – Seite 545/726 Bellmansches Optimalitätsprinzip Wie kann die letzte Position aussehen? Restproblem xi xi − yj − yj P (i − 1, j − 1) P (i − 1, j) Nachdem über die letzte Position entschieden ist, kann das Restproblem optimal gelöst werden. Bellmansche Optimalitätsgleichung P (i, j − 1) Einfache Anfangsprobleme P (i, 0) → einzige Lösung P (0, j) analog. x1 − ... ... . – Seite 546/726 xi − Kosten i · d. C(i, j) = min{C(i − 1, j − 1) + c(xi , yj ), min i A(i, j) = x yj C(i − 1, j) + d, min C(i, j − 1) + d} min A(i, j) = xi − A(i, j) = − yj O(1) Tabelle (C(i, j), A(i, j)) zeilenweise von oben nach unten füllen, n × m-Matrizen. Zeit und Platz O(nm). . – Seite 547/726 . – Seite 548/726 Im Anschluss optimales Alignment in Zeit O(n + m) berechenbar. 5.4 Single Source Shortest Paths (SSSP) – Algorithmus von Dijkstra Hier gibt es Spezialtricks: Eingabe: C = (c(i, j))1≤i,j≤n mit c(i, j) ≥ 0 und c(i, i) = 0 wie beim APSP. Zusätzlich Startpunkt s. Platz : O(nm) → O(n + m). Zeit : O(nm) → O(nm), konstanter Faktor wächst. Ziel: Kosten d(i) billigster Wege von s nach i, V (i) Vorgänger von i auf billigstem Weg von s nach i. Hier Vorgänger j von i und nicht Nachfolger k von s, da wir billigsten Weg von s nach j berechnen, aber nicht billigsten Weg von k nach i. . – Seite 549/726 Natürlich löst der APSP-Algorithmus auch das SSSP-Problem, aber er berechnet viel ungefragte Information; daher wollen wir effizienter sein. . – Seite 550/726 greedy: Suche jeweils nach dem nächstgelegenen Ort unter den Orten i, für die d(i) noch nicht berechnet wurde, behandle also die Orte in einer Reihenfolge i1 , . . . , in mit d(i1 ) ≤ · · · ≤ d(in ). (Diese Reihenfolge ist zu Beginn unbekannt.) Der Algorithmus von Dijkstra folgt weder einer greedy Strategie in Reinkultur noch der dynamischen Programmierung in Reinkultur. Er zieht Nutzen aus beiden Entwurfsmethoden. dynamische Programmierung: Schränke die Menge erlaubter Zwischenknoten ein, nicht auf 1, . . . , k , sondern auf i1 , . . . , ik . (Wie die Einschränkung konkret aussieht, ist zu Beginn unbekannt.) . – Seite 551/726 . – Seite 552/726 Beispiel für allgemeines k : Zwischenziel nach Phase k : A(k) – Wir kennen die Orte i1 , . . . , ik , es sei A(k) = {i1 , . . . , ik }. – Wir kennen d(i) und V (i) für i ∈ A(k). – Wir kennen dk (i) und Vk (i) für i 6∈ A(k), das ist die Distanz billigster Wege von s nach i mit Zwischenknoten aus A(k) bzw. der Vorgänger von i auf einem derartigen Weg. k = 1 : A(1) := {s}, d(s) := 0, V (s) := nil (Weg hat Länge 0), für i 6∈ A(1) : d1 (i) := c(s, i), V1 (i) := s. i1 = s i2 i3 i4 i5 i6 d(i) {1, . . . , n} − A(k) dk (i) Vk (i) 0 3 3 5 6 6 x x x x x x x x w 10 8 11 12 10 10 8 9 nach Voraussetzung ≥ i2 i3 i3 i3 i1 i6 i4 i1 Kandidaten für i7 6 Kann es einen Weg von s nach w mit Kosten < 8 geben? . – Seite 553/726 . – Seite 554/726 Nein! A(k) Dieses Argument gilt allgemein: {1, . . . , n} − A(k) v s Also: Wähle einen Knoten v als ik+1 , der unter allen Knoten in {1, . . . , n} − A(k) einen minimalen dk (·)-Wert hat. Update der Informationen: u w – A(k + 1) := A(k) ∪ {ik+1 }, d(ik+1 ) := dk (ik+1 ), V (ik+1 ) := Vk (ik+1 ). Jeder Weg s → w verlässt A(k) irgendwann zum ersten Mal, z. B. über die Kante (u, v). Kosten s → u : d(u) (optimal nur in A(k)). Kosten u → v direkt : c(u, v). Gesamtkosten ≥ d(u) + c(u, v) ≥ dk (v) nach Definition ≥8 im Beispiel. . – Seite 555/726 . – Seite 556/726 – Sei j ∈ {1, . . . , n} − A(k + 1). Wir kennen billigsten Weg s → j mit Zwischenorten aus {1, . . . , ik }. Wir suchen billigsten Weg s → j mit Zwischenorten aus {1, . . . , ik , ik+1 }. Entweder ik+1 nicht auf dem Weg oder ik+1 auf dem Weg. Bellmansches Optimalitätsprinzip: Wenn ik+1 auf dem Weg, dann Weg s → ik+1 so billig wie möglich. Außerdem von ik+1 direkt zu j , zu allen anderen Knoten aus {1, . . . , n} − A(k + 1) hätte man ohne ik+1 gehen können, ohne Kosten zu erhöhen. Bellmansche 0ptimalitätsgleichung dk+1 (j) := min {dk (i), d(ik+1 ) + c(ik+1 , j)} Wege ohne ik+1 Wege mit ik+1 I II ( Vk (j) , falls I ≤ II Vk+1 (j) := ik+1 , sonst. O(1) Es ist A(n) = {1, . . . , n} und dann haben wir die gewünschten Informationen d(1), . . . , d(n), V (1), . . . , V (n). . – Seite 557/726 . – Seite 558/726 Rechenzeitanalyse Rechenzeit bei wenigen Kanten (Adjazenzlisten) A(k) → A(k + 1) : O(n), da ≤ n j -Werte, jeweils O(1), für Berechnung von ik+1 . 2 k ∈ {1, . . . , n} → O(n ). Überschreibe dk - und Vk -Werte mit dk+1 - und Vk+1 -Werten. Das spart nicht nur Platz, sondern auch Zeit: Sei ik+1 = j und i 6∈ Adj(j): APSP =∞ z }| { dk+1 (j) = min{dk (i), dk (ik+1 ) + c(ik+1 , i)} = dk (i) Für jeden Startpunkt s ∈ {1, . . . , n} Algo. von Dijkstra anwenden. O(n3 ) Aber der Algorithmus aus Kap. 5.3 ist effizienter (konstanter Faktor). . – Seite 559/726 und Vk+1 (i) = Vk (i). Überschreiben nicht nötig, die alten Informationen sind korrekt. . – Seite 560/726 Min-Heap für die Knoten in {1, . . . , n} − A(k) mit aktuellem dk -Wert. Also: Durchlaufe Adj(j) und aktualisiere die Werte nur für i ∈ Adj(j). – Minimumberechnung in O(1), reheap in O(log n). → Zeit O(|Adj(j)|). – Aktualisierung der dk -Werte für jede Kante einmal → verallgemeinerte Heapoperation → O(log n). → Jedes j nur einmal ik+1 -Wert. P → Gesamtzeit O(|Adj(j)|) = O(m). j −→ O(n + m log n) für SSSP. −→ O(n2 + nm log n) für APSP. Aber: Berechnung des ik -Wertes: Minimum von n − k + 1 Zahlen. → Zeit O(n2 ). Geeignete Datenstruktur? . – Seite 561/726 Wenn nur der billigste Weg s → t berechnet werden soll: . – Seite 562/726 5.5 Branch-and-Bound Algorithmen – Algo. von Dijkstra, Voraussetzungen: – Abbruch, wenn t in die A-Menge aufgenommen wird und d(t) und V (t) berechnet worden sind. – Optimierungsproblem. – Endliche Menge möglicher Lösungen. – Lösungsmenge kann aufgeteilt werden, so dass sich wieder Probleme vom selben Typ ergeben. – Folgende Module sind effizient ausführbar. . – Seite 563/726 . – Seite 564/726 Beschreibung für Maximierungsprobleme Nebenbemerkung: Upper Bound Modul Berechne (möglichst gute) obere Schranke U für den Wert einer optimalen Lösung, z. B. durch exakte Lösung einer relaxierten Variante (weniger Nebenbedingungen). Der Wert dieser Lösung ist maximal U − L weit vom Optimum entfernt, die Approximationsgüte ist durch U/L beschränkt. Falls U = L, ist das Problem gelöst. Falls U − L > 0, aber klein genug oder U/L klein genug, kann gestoppt werden. Wir haben eine Lösung mit garantierter Güte. Falls U − L > 0, aber nicht klein genug, weiter machen. Lower Bound Modul Berechne (möglichst gute) untere Schranke L für den Wert einer optimalen Lösung. Dies sollte konstruktiv erfolgen, z. B. durch Anwendung einer Heuristik, die eine zulässige Lösung liefert. . – Seite 565/726 . – Seite 566/726 Branching Modul Zerlegung des Problems P in Teilprobleme P1 , . . . , Pk , so dass für die Lösungsmenge L(P ) gilt: [ L(P ) = L(Pi ). Dabei ist U triviale obere Schranke und wir fordern Ui ≤ U für alle i. Für das Teilproblem Pj , das die Lösung mit Wert L enthält, ist L eine triviale untere Schranke und wir fordern L ≤ Lj für dieses j . 1≤i≤k Lösungen für Pi haben für Pi und P denselben Wert. Neue Schranken für das Gesamtproblem: – Möglichst disjunkte Probleme. U ∗ = max{Ui |1 ≤ i ≤ k} ≤ U, – Möglichst gleich große / schwere Teilprobleme. – Möglichst kleines k . L∗ = max{Li |1 ≤ i ≤ k} ≥ L. Berechne Schranken Ui und Li für Pi . Also U ∗ − L∗ ≤ U − L und U ∗ /L∗ ≤ U/L. . – Seite 567/726 . – Seite 568/726 Falls |L(Pi )| = 1, sollte Ui = Li der Wert dieser einzigen Lösung sein. Search Modul Welches Problem sollen wir zerlegen? Wenn alle Teilprobleme einelementig sind, gilt Ui = Li für alle unzerlegten Probleme und U = L. Dann spätestens sind wir fertig. → expo. Zeit. Strategie 1 mit dem Ziel, den Wert von U zu verkleinern. Die Hoffnung ist, dass wir eher fertig sind. – Wähle unzerlegtes Problem mit Ui = U : Warum? – Dort ist am meisten zu holen. Falls Ui ≤ L, kennen wir eine Lösung mit Wert L, Teilproblem Pi kann nur Lösungen mit Maximalwert Ui liefern, also können wir darauf verzichten, Pi zu zerlegen. – Falls U nicht Wert optimaler Lösung, muss dieses Problem irgendwann sowieso zerlegt werden. . – Seite 569/726 . – Seite 570/726 Learning Modul Strategie 2 mit dem Ziel, den Wert von L zu erhöhen. – Heuristiken liefern schlechte Lösungen. zur Vermeidung unnötiger Problemzerlegungen. – Versuche, Erfolg versprechenden Weg zu verfolgen, bis Teilproblem einelementig. In einem Teilproblem getroffene Entscheidungen können andere Entscheidungen implizieren. Diese sollten vorab ausgeführt werden. – Wähle also möglichst kleine Probleme mit einer großen oberen Schranke. Jede Strategie ist erlaubt, problemangepasste Phantasie ist gefragt. . – Seite 571/726 Und wie geht das konkret? . – Seite 572/726 Branching Modul Inklusion und Exklusion des Objektes i. Rucksackproblem (KP) neues Gewichtslimit G := G − gi , bereits erreichter Nutzen v := v + vi (v mit 0 initialisiert). Inklusion: λi := 1, Optimale greedy Strategie für das relaxierte KP mit 0 ≤ λi ≤ 1 (Objektzerlegung). Upper Bound Modul: Exklusion: λi := 0, Gewichtslimit und erreichter Nutzen unverändert. Preprocessing O(n log n) für das Sortieren der Effektivitätswerte. Danach jedes Mal O(n). Greedy Strategie für das unrelaxierte KP. Sie liefert eine konstruktive untere Schranke, die oft nicht ganz schlecht ist. Ohne Preprocessing (s. o.) Zeit O(n). Lower Bound Modul: Disjunkte Zerlegung in zwei gleich große Lösungsmengen. Welches Objekt wählen wir? Dies ist eine wichtige Entscheidung. Ziel U senken, also sollte die Lösung des relaxierten Problems nicht in den relaxierten Teilproblemen möglich sein. . – Seite 573/726 Nur das bei der Lösung „zerschnittene“ Objekt hat nicht den λ-Wert 0 oder 1. Dieses Objekt wird gewählt: – bei λi = 0 wird Objekt i ausgelassen und ein „späteres“ Objekt (in der Sortierung nach Effektivitätswerten) zerlegt, – bei λi = 1 wird Objekt i erzwungen, das Gewichtslimit ist um gi gesunken und ein „früheres“ Objekt wird zerlegt. . – Seite 575/726 . – Seite 574/726 Search Modul Greedy Lösungen im worst case sehr schlecht, aber U − L ≤ vi , falls Objekt i zerlegt wird. Oft greedy Lösungen „ganz gut“ −→ Zerlege stets das unzerlegte Teilproblem mit der größten oberen Schranke. Learning Modul Hier nicht sehr mächtig. Setze λj = 0 für alle Objekte j , die das aktuelle Gewichtslimit übertreffen. . – Seite 576/726 Beispiel – Ik enthält alle in Pk durch Inklusion erzwungenen Objekte. P1 : I1 = ∅, E1 = ∅, U1 = 88, L1 = 83, M1 = {1, 2, 3, 4, 6, 7, 8}. P2 : I2 = {5}, E2 = ∅, U2 = 87, L2 = 82, M2 = {1, 2, 3, 5, 7}. – Ek enthält alle in Pk durch Exklusion verbotenen Objekte. P3 : I3 = ∅, E3 = {5}, U3 = 83, L3 = 83, M3 = {1, 2, 3, 4, 6, 7, 8}. – Uk obere Schranke für Pk . – Lk untere Schranke für Pk . U = 87, L = 83, Zerlegung von P2 . – Mk Menge der vom greedy Algorithmus für Pk ausgewählten Objekte. P4 : I4 = {4, 5}, E4 = ∅, U4 = 86, L4 = 82, M4 = {1, 2, 4, 5, 8}. P5 : I5 = {5}, E5 = {4}, U5 = 85, L5 = 82, M5 = {1, 2, 3, 5, 7}. U = 86, L = 83, Zerlegung von P4 . n = 10, G = 16 Objekt 1 2 3 4 5 6 7 8 9 10 Nutzen 20 28 10 12 21 9 3 1 2 1 Gewicht Effektivität 1 4 2 3 7 3 2 1 4 3 20 7 5 4 3 3 3/2 1 1/2 1/3 . – Seite 577/726 . – Seite 578/726 U = 84, L = 83, Zerlegung von P6 . P10 : I10 = {2, 3, 4, 5}, E10 = ∅, U10 = 71, L10 = 71, M10 = {2, 3, 4, 5}. P6 : I6 = {3, 4, 5}, E6 = ∅, U6 = 84, L6 = 72, M6 = {1, 3, 4, 5, 6}. P11 : I11 = {3, 4, 5}, E11 = {2}, U11 = 72, L11 = 72, M11 = {1, 3, 4, 5, 6}. P7 : I7 = {4, 5}, E7 = {3}, U7 = 84, L7 = 82, M7 = {1, 2, 4, 5, 8}. U = 84, L = 83, Zerlegung von P7 . U = 85, L = 83, Zerlegung von P5 . P12 : I12 = {4, 5, 6}, E12 = {3}, U12 = 76, L12 = 65, M12 = {1, 4, 5, 6, 7}. P8 : I8 = {5, 6}, E8 = {4}, U8 = 83, L8 = 79, M8 = {1, 2, 5, 6, 8}. P13 : I13 = {4, 5}, E13 = {3, 6}, U13 = 82, L13 = 82, M13 = {1, 2, 4, 5, 8}. U = 83, L = 83, STOP. P9 : I9 = {5}, E9 = {4, 6}, U9 = 82, L9 = 82, M9 = {1, 2, 3, 5, 7}. P8 und P9 können schon gestrichen werden, da ihre oberen Schranken die beste untere Schranke nicht übertreffen. . – Seite 579/726 M1 = {1, 2, 3, 4, 6, 7, 8} ist optimal. . – Seite 580/726 5.6 Divide-and-conquer Algorithmen Bei Quicksort und Quickselect ist vorher unklar, wie groß die Teilprobleme werden. Voraussetzungen Häufig: a Teilprobleme der Größe n/b und Extraaufwand cn. – Problemzerlegung effizient berechenbar (im Gegensatz zur dynamischen Programmierung). −→ R(1) = c R(n) = a · R(n/b) + cn (Annahme n = bk , d. h. k = logb n) – Rekursive Lösung der Teilprobleme. – Effiziente Berechnung der Gesamtlösung aus den Teilproblemen. Wie löst man diese Rekursionsgleichung? Beispiele: Maxsummenproblem, binäre Suche, Quicksort, Quickselect, Mergesort und Batchersort (enthält mit Batchermerge einen weiteren divide-and-conquer Algorithmus). . – Seite 581/726 . – Seite 582/726 1. Fall: a < b. Allgemeiner Ansatz k k−1 R b =a·R b + cbk 2 k−2 =a ·R b + acbk−1 + cbk 3 k−3 =a ·R b + a2 cbk−2 + acbk−1 + cbk = ak · R(1) + ak−1 cb + ak−2 cb2 + · · · + a2 cbk−2 + acbk−1 + cbk (Induktionsbeweis) a k a a 2 k k R b + ··· + = cb 1 + + b b b k+1 1 − ab 1 ≤ cn = cn a 1− b 1 − ab b = c n = Θ(n). b−a =⇒ R bk = c ak + ak−1 b + ak−2 b2 + · · · + a2 bk−2 + abk−1 + bk . . – Seite 583/726 . – Seite 584/726 2. Fall: a = b. 3. Fall: a > b. k ! 2 b b b R(bk ) = cak 1 + + + ··· + a a a b k+1 1 − a k a = cak a . ≤c b a−b 1− a R(bk ) = c(k + 1)bk = cn(logb n + 1) = Θ(n log n). Es ist Also ak = alogb n = nlogb a . a R bk ≤ c a−b nlogb a = Θ nlogb a . . – Seite 585/726 Fazit: . – Seite 586/726 5.7 Matrixmultiplikation – c wirkt sich nur als konstanter Faktor aus. Problemstellung gut bekannt. – Entscheidend ist das Verhältnis von a zu b. Z = X · Y für n × n-Matrizen, also P zij = xik ykj n Mult., n − 1 Add. – Was ist besser? 17 Teilprobleme der Größe n/9 1≤k≤n −→ oder 24 Teilprobleme der Größe n/12 log9 17 = log 17 log 9 log12 24 = Dagegen Z 0 = X + Y mit ≈ 1, 289 . . . log 24 log 12 n3 Mult., n3 − n2 Add. → O n3 . 0 Mult. und n2 Add. → O n2 . Matrixmultiplikation in o n3 ? ≈ 1, 279 . . . . . – Seite 587/726 . – Seite 588/726 Sei n = 2k Multiplikation von 2 × 2-Matrizen Klassisch = = = = = = = 4 Add. 7 Mult. Strassen m1 m2 m3 m4 m5 m6 m7 8 Mult. 12 Add. (x12 − x22 ) · (y21 + y22 ) (x11 + x22 ) · (y11 + y22 ) (x21 − x11 ) · (y11 + y12 ) (x11 + x12 ) · y22 x11 · (y12 − y22 ) x22 · (y21 − y11 ) (x21 + x22 ) · y11 6 Subtrakt. z11 z12 z21 z22 = = = = m1 + m2 − m 4 + m6 m4 + m5 m6 + m7 m2 + m3 + m5 − m 7 X X11 X12 X21 X22 zij aus Z12 zij = X · Y Y11 Y12 Y21 Y22 xik ykj + 1≤k≤n/2 | {z } Position (i, j − n/2) aus X11 · Y12 = Z Z11 Z12 Z21 Z22 n/2 × n/2-Matrix X xik ykj = Position (i, j − n/2) aus X11 · Y12 + X12 · Y22 . n/2<k≤n | {z } Position (i, j − n/2) aus X12 · Y22 Ja, und . . . ? . – Seite 589/726 . – Seite 590/726 Klassisch: X X11 X12 X21 X22 Y · Y11 Y12 Y21 Y22 Z = X11 · Y11 + X12 · Y21 X11 · Y12 + X12 · Y22 X21 · Y11 + X22 · Y21 X21 · Y12 + X22 · Y22 Wie Multiplikation von 2 × 2-Matrizen, aber nun sind die Operationen Matrixaddition und Matrixmultiplikation von (n/2) × (n/2)-Matrizen. 2n3 − n2 arithmetische Operationen Neu (Methode von Strassen) n 3 n 2 n 2 7· 2 − + 18 · 2 2 2 7 3 11 2 = n + n 4 4 Es gilt Es lohnt sich, eine Matrixmultiplikation einzusparen. 7 3 11 2 n + n < 2n3 − n2 4 4 15 2 1 3 n < n ⇔ 4 4 ⇔ n > 15. Noch besser: Methode von Strassen auch rekursiv benutzen. . – Seite 591/726 . – Seite 592/726 Nun zur Analyse Problemgröße N = n2 , n = 2k . Genauere Analyse: C(N ): Anzahl arithmetischer Operationen zur Multiplikation von N × N -Matrizen 2 C(1) = 1, C(N ) = 7 · C(N/4) + 18 · N4 ( N4 ersetzt n2 ) = 7 · C(N/4) + 92 · N Was wissen wir aus dem allgemeinen Ansatz? D(1) = ⇒ 9 2 , D(N ) = 7 · D(N/4) + C(N ) ≤ D(N ) = 9 2 · 7k (1− 47 ) 9 2 C(n2 ) = 7nlog 7 − 6n2 . Mix-Methode: Verwende Strassenmethode, bis Teilprobleme der Größe m × m mit m ≤ 15 auftauchen, ·N dann die klassische Methode. k+1 1− 47 ≤ = 9 7 21 k k 2 · 3 ·7 = 2 ·7 21 log 7 2,81... ≈ 21 2n 2n . – Seite 593/726 5.8 Schnelle Fouriertransformation (Fast Fourier Transform FFT) – Aus Bild- und Signalverarbeitung nicht wegzudenken. Basisobjekte: Polynome (n − 1)-ten Grades: f (x) = an−1 xn−1 + · · · + a1 x1 + a0 . . – Seite 594/726 Hauptsatz der Algebra: Polynome (n − 1)-ten Grades sind durch n Funktionswerte eindeutig bestimmt. Beide Darstellungsformen haben Vor- und Nachteile: – Funktionsauswertung: Koeffizientendarstellung besser. Darstellungsformen oder auch „Datenstrukturen“: – Addition zweier Polynome: Beides gut. – Koeffizientendarstellung: (a0 , a1 , . . . , an−1 ), – Darstellung durch Funktionswerte an n verschiedenen Stellen z1 , . . . , zn : ((z1 , f (z1 )), . . . , (zn , f (zn ))), wobei im Allgemeinen z1 , . . . , zn fest vereinbart sind. . – Seite 595/726 – Differenzieren oder Integrieren: Koeffizientendarstellung besser. – Multiplikation zweier Polynome: Wenn jeweils 2n Funktionswerte vorliegen → O(n). . – Seite 596/726 Alternative: Multiplikation in Koeffizientendarstellung: an−1 xn−1 + · · · + a1 x + a0 · bn−1 xn−1 + · · · + b1 x + b0 = c2n−2 x2n−2 + · · · + c1 x + c0 mit ck = P ai b j – f , g in Koeffizientendarstellung. – Berechne 2n Funktionswerte von f und g (an denselben Stellen). – Berechne 2n Funktionswerte von f · g . (Konvolution) 0≤i,j≤n−1 i+j=k O(n) – Berechne Koeffizientendarstellung von f · g . → O(n2 ) Also brauchen wir effiziente Transformationen zwischen den Darstellungen. . – Seite 597/726 – f gegeben durch (an−1 , . . . , a0 ) → (f (z1 ), . . . , f (zn )). Unsere Chance: Wir dürfen z1 , . . . , zn selber wählen. Wir wählen für ein w die Stellen w 0 = 1, w, w2 , . . . , wn−1 . – f gegeben durch (f (z1 ), . . . , f (zn )) → (an−1 , . . . , a0 ). Aber welches w? Primitive n-te Einheitswurzel in einem kommutativen Ring mit Einselement, d. h. – w 6= 1, – wn = 1, P – ∀1 ≤ k ≤ n − 1 : wjk = 0. Wir behandeln die erste Transformation. f (zi ) = an−1 · zin−1 + · · · + a1 zi + a0 → f (z1 ), . . . , f (zn ) . – Seite 598/726 O(n) O(n2 ). 0≤j≤n−1 Das ist zu langsam. Exkurs: Gibt es sowas überhaupt? . – Seite 599/726 . – Seite 600/726 Beispiel 1: Beispiel 2: C R Nur für n = 2 ist w = −1 geeignet. Forderung wn n>1: = 1 ⇒ w = 1 (verboten) oder w = −1. ei2π/n ist geeignet (i = √ −1 = imaginäre Einheit) Nach Definition eix = cos x + i sin x. Dritte Bedingung n = 2 : (−1)0 + (−1)1 = 0 n > 2 : Wähle k = 2, alle Summanden sind 1 ⇒ Forderung unerfüllbar. – n = 2 : ei2π/n = −1 6= 1. – 2π n > 2 : ei2π/n = cos 2π n + i sin n 6= 1. | {z } ei2π/n n 6=0 = ei2π = cos 2π + i sin 2π = 1. . – Seite 601/726 . – Seite 602/726 Geometrisch – P (ei2π/n )jk = 0≤j≤n−1 P (ei2πk/n )j |eix | = | cos x + i sin x| = (cos2 x + sin2 x)1/2 = 1, also eix auf dem Einheitskreis der gaußschen Zahlenebene. 0≤j≤n−1 = (ei2πk/n )n −1 ei2πk/n −1 i (geometr. Reihe) Zähler = 0, da (ei2πk/n )n = ei2πk = cos 2πk + i sin 2πk = 1 ei2π/n Winkel 2π/n 1 2πk Nenner 6= 0, da ei2πk/n = cos 2πk n + i sin n 6= 1. eix · eiy = ei(x+y) → Winkel addieren sich mod 2π . Eigenschaft 3: Winkel teilen Vollkreis in gleich große Segmente, Kräfte heben sich auf (Vektoraddition). . – Seite 603/726 . – Seite 604/726 Aber: Rechnen mit cos und sin führt zu Rundungsfehlern. Diskrete Fouriertransformation Beispiel 3: a → DFTn (a) Zm Falls n und w Zweierpotenzen und m = wn/2 + 1, dann ist w geeignet. (ohne Beweis.) (a0 , . . . , an−1 ) → (f (w 0 ), . . . , f (wn−1 )) für eine primitive n-te Einheitswurzel. P Genauer f (wj ) = ai wij 0≤i≤n−1 → DFTn (a) = W · a (Matrix-Vektor-Produkt) mit W = (wij ). . – Seite 605/726 Sei n = 2k (eventuell a mit Nullen ergänzen). Auswertung von f1 und f2 an den Stellen w0 , w2 , w4 , . . . , wn−2 , wn , wn+2 , wn+4 , . . . , wn+n−2 k k k k 0 2 4 n−2 w w , da wn = 1. w w f (wj ) = a0 + a1 wj + a2 w2j + · · · + an−1 w(n−1)j = [a0 + a2 w2j + a4 w4j + · · · + an−2 w(n−2)j ] + [a1 wj + a3 w3j + a5 w5j + · · · + an−1 w(n−1)j ] X X a2i (w2 )ij + wj · a2i+1 (w2 )ij . = 0≤i≤n/2−1 ∧ f1 = (a0 , a2 , . . . , an−2 ) . – Seite 606/726 0≤i≤n/2−1 Also 2 Polynome, Grad n/2 − 1, n/2 Funktionswerte. → Divide-and-conquer. Damit das rekursiv weiter geht, muss w2 eine primitive (n/2)-te Einheitswurzel sein. ∧ f2 = (a1 , a3 , . . . , an−1 ) . – Seite 607/726 . – Seite 608/726 Da w primitive n-te Einheitswurzel, gilt: Eigenschaft 1: – Falls w2 = 1, hat die Summe aller w 2j nicht den Wert 0. 0 = w0 + w2k + · · · + w(n−1)2k = w0 + w2k + · · · + w(n/2−1)2k + h i (n/2)2k 0 2k (n/2−1)2k |w {z } · w + w + · · · + w Eigenschaft 2: – (w2 )n/2 = wn = 1. (wn )k =1 0 = 2· w +w Eigenschaft 3: – Sei 1 ≤ k ≤ n/2 − 1 (⇒ 1 ≤ 2k ≤ n − 1) P ! (w2 )jk = w0 + w2k + · · · + w(n/2−1)2k = 0. 2k + ··· + w (n/2−1)2k In C und Zm , m ungerade, gibt es 2−1 . Also folgt 0≤j≤n/2−1 0 = w 0 + w2k + · · · + w(n/2−1)2k . . – Seite 609/726 Preprocessing: Berechne w2 , . . . , wn−1 und speichere die Werte in Array. n = 1: f (1) = a0 n = 2k , k ≥ 1: 2 Probleme halber Größe f (wj ) = f1 (wj ) + wj · f2 (wj ) n−2 Multiplikationen 0 arithm. Operationen 2 arithm. Operationen (für j = 0, nur eine) → zus. 2n − 1 Operationen. . – Seite 611/726 . – Seite 610/726 −→ C(1) = 0, C(n) = 2 · C(n/2) + 2n − 1. Hinzu kommen n − 2 Operationen für das Preprocessing. . – Seite 612/726 Wir kennen Was ist mit der Rücktransformation? DFTn (a) = W · a =⇒ a = W −1 · DFTn (a), falls W −1 existiert. D(1) = 2, D(n) = 2 · D(n/2) + 2n −→ D(n) = 2n log n + 2n. In C und Zm , m ungerade, existiert für Zweierpotenzen n−1 Offensichtlich C(n) < D(n). Behauptung: Genaue Rechnung zeigt: – w−1 := wn−1 ist primitive n-te Einheitswurzel. Für DFTn , n = 2k ≥ 2, genügen 2n log n − 1 arithmetische Operationen. – W −1 = n1 (w−ij ). −→ FFT auch auf Rücktransformation anwendbar. . – Seite 613/726 . – Seite 614/726 Beweis zu Inverse von W hat die Einträge n1 w−ij . Beweis durch Nachrechnen: W 0 = n1 (w−ij ). 0 Z =W ·W P P zjj = wjm · n1 · w−mj = n1 1 = 1. Beweis zu w−1 ist primitive n-te Einheitswurzel. – w 6= 1 =⇒ w−1 6= 1. 0≤m≤n−1 – wn = 1 =⇒ (w−1 )n = (wn )−1 = 1. – (w−1 )0 + (w−1 )k + (w−1 )2k + · · · + (w−1 )(n−1)k = [(w−1 )0 + (w−1 )k + (w−1 )2k + · · · + (w−1 )(n−1)k ] · wnk + w(n−1)k + w(n−2)k + · · · + wk +w0 = 0 j 6= k zjk = →1 z}|{ wnk = P 0≤m≤n−1 0≤m≤n−1 wjm · 1 n · w−mk = 1 n P (wj−k )m = 0 0≤m≤n−1 j > k , da w primitive n-te Einheitswurzel, j < k , da w−1 primitive n-te Einheitswurzel. beides 1 , da w primitive n-te Einheitswurzel. . – Seite 615/726 . – Seite 616/726 n=8 a0 + a1 wj + a2 w2j + a3 w3j + a4 w4j + a5 w5j + a6 w6j + a7 w7j wx w {| ! '( MN y VU }~ yz U QR => · [a3 + a7 w ] ] ] &^] ^] 5.9 Nächste Nachbarn in der Ebene 4j · [a2 + a6 w ] + w · [a1 + a5 w ] + w 2j ?@ 4j ST j AB 4j KL YZ GH K "# ¥¦ qr st ¡¢ u JI uv § § \[ ª © ¨§ § & ;< -. 12 £¤ «¬ ·¸ ³´ ¯° kl ­® ef _` ¥ mn ±² op cd 78 Θ(n2 ) → Θ(n log n): Für große, praktisch relevante n: undurchführbar → durchführbar. )* +, /0 j = 0, 1, 2, 3, 4, 5, 6, 7 65 g ab :9 3 gh i 34 ij EF Veranschaulichung der Einsparungen j = 0, 1, 2, 3 $ $ %$ %$ & WX OP µ¶ j = 0, 1 = [a0 + a4 w ] + w 2j CD 4j Welche Punkte haben den kleinsten Abstand? . – Seite 617/726 . – Seite 618/726 Wie ist hier divide-and-conquer möglich? Zum Aufwärmen: n Punkte auf der Geraden, d-dimensional für d = 1. (Natürlich könnte man das Problem über Sortieren lösen.) Problemstellung ganz einfach zu verstehen. Motivation: Mindestabstände einhalten, z. B. auf Chip. Auch hier: n2 −→ O(n log n). n/2 Punkte n/2 Punkte min. Abstand δ2 min. Abstand δ1 trivial δ 0 = min{δ1 , δ2 } δ0 δ0 ≤ 1 Punkt pro Menge Das Problem in der Mitte ist „fast“ 0-dimensional. . – Seite 619/726 . – Seite 620/726 Nun d = 2, Zerlegung der Punkte in zwei Hälften gemäß x-Koordinate. Löse Teilprobleme: δ1 und δ2 , δ 0 = min{δ1 , δ2 }. C Algorithmus für n = 2k : – Sortiere Punkte p1 = (x1 , y1 ), . . . , pn = (xn , yn ) bezüglich x-Koordinate, nach Umnummerierung x1 ≤ x 2 ≤ · · · ≤ x n . O(n log n) D δ0 X p δ0 A – Iterativer Aufbau Rechteck ABCD kann höchstens 6 Punkte enthalten. Runde 1: Betrachte n/2 Punktmengen der Größe 2: (p1 , p2 ), (p3 , p4 ), . . . , (pn−1 , pn ). .. . B Runde i: Betrachte n/2i Punktmengen der Größe 2i : (p1 , . . . , p2i ), (p2i +1 , . . . , p2·2i ), . . . M − δ0 M M + δ0 Dieser schmale vertikale Streifen ist „fast“ 1-dimensional. . – Seite 621/726 Ziel nach Runde i: – Punkte jeder Punktmenge der Größe 2i sind in einem Array bzgl. y -Koordinate sortiert. – Für Punkte jeder Punktmenge der Größe 2i ist der Minimalabstand zweier Punkte bekannt. . – Seite 622/726 Sei Runde i − 1 erfolgreich beendet. Implementierung von Runde i in Zeit O(n), dann Gesamtzeit O(n log n), da es k = log n Runden gibt. Genügt zu zeigen: P1 mit 2i−1 Punkten i = 1: P2 mit 2i−1 Punkten – Die beiden Punkte jeder Punktmenge werden bzgl. y -Koordinate verglichen. → P1 ∪ P2 mit 2i Punkten in Zeit O(2i ). Setze m = 2i−1 . – Der Abstand der beiden Punkte wird berechnet. → O(n) . – Seite 623/726 . – Seite 624/726 Sortierung bzgl. y -Koordinate mit Reißverschlussverfahren, da Sortierungen für P1 und P2 vorliegen. → O(m). P1 P2 δ0 δ0 Nur Punkte p1 ∈ P1 ∩ S und p2 ∈ P2 ∩ S interessieren. Was bleibt? Minimaler Abstand in P1 ∪ P2 . Bekannt: δ1 minimaler Abstand in P1 , δ2 minimaler Abstand in P2 , δ 0 = min{δ1 , δ2 }. Gesucht: δ = min({δ 0 } ∪ {d(p1 , p2 ) | p1 ∈ P1 , p2 ∈ P2 }). Diese Punkte können aus den nach y -Koordinaten sortierten Folgen herausgefiltert werden → O(m). . – Seite 625/726 | {z Streifen S } . – Seite 626/726 – Sei uj−1 bearbeitet, dabei vs letzter Punkt mit y(vs ) ≤ y(uj−1 ) + δ 0 . – Durchlaufe Liste der Punkte in P1 ∩ S nach aufsteigender y -Koordinate, nenne sie u1 , u2 , . . . , ul (l ≤ m, l m möglich). – Betrachte Liste der Punkte in P2 ∩ S nach aufsteigender y -Koordinate: v1 , . . . , vr . – Für u1 finde alle Punkte vi mit y(u1 ) − δ 0 ≤ y(vi ) ≤ y(u1 ) + δ 0 . Berechne Abstand von u1 und vi und aktualisiere δ 0 gegebenenfalls. – Für uj sind nur die Punkte ab vs−5 interessant. (Nur ≤ 6 Punkte im interessanten Bereich.) – Finde größtes s0 mit y(vs0 ) ≤ y(uj ) + δ 0 , berechne Abstände zu uj und aktualisiere δ 0 gegebenenfalls. Korrektheit: klar. Rechenzeit: O(m), da wir pro uj -Knoten nur ≤ 6 Schritte rückwärts gehen, gibt es ≤ 6m Rückwärtsschritte und damit ≤ 7m Vorwärtsschritte (amortisierte Analyse). . – Seite 627/726 . – Seite 628/726 Beispiel: Rechteckmaßproblem 5.10 Sweepline Technik Probleme der algorithmischen Geometrie haben viele Anwendungen, besonders in graphischen Systemen. 9 Rechtecke → 18 x-Koordinaten 18 y -Koordinaten Dazu gehört auch Kap. 5.9. Hier eine allgemeine Technik für zweidimensionale Probleme. Sweepline ist vertikal, überstreicht die Ebene von links nach rechts, hält an Ereignispunkten. (Sweepplane für dreidimensionale Probleme.) Berechne die überdeckte Fläche (nicht die Summe der Rechteckflächen). . – Seite 629/726 Ereignispunkte: x-Koordinaten, wo Rechtecke beginnen oder enden. Also: Wir wollen l1 , . . . , l2n−1 berechnen. Dann nur noch: Alle li · (xi+1 − xi ) summieren. Also 2n Ereignispunkte bei n Rechtecken. Sortiere diese, so dass x1 ≤ x2 ≤ · · · ≤ x2n . . – Seite 630/726 O(n log n) Wie sieht das Problem zwischen zwei Ereignispunkten aus? xi xi+1 Informationen aus der Berechnung von li−1 nutzen. Die Änderungen sind ja gering. Entweder wird ein Rechteck aktiv oder passiv. Allerdings könnten Teilbereiche bereits aktiv sein oder aktiv bleiben. Länge li der Abschnitte der Sweepline, die in Rechtecken liegen, ist konstant. Also Fläche der Rechtecke in diesem Bereich: li · (xi+1 − xi ). Sweepline . – Seite 631/726 . – Seite 632/726 Zusatzinformationen an v für [i, j]: Verwende Segmentbaum mit Zusatzinformationen. – Sortiere y -Koordinaten, wo Rechtecke beginnen oder enden. y1 ≤ y2 ≤ · · · ≤ y2n . O(n log n) – Früher bei Segmentbäumen Grundmenge [1, n], Intervalle an den Blättern [i, i], hier [1, 2n], Aufteilung in angrenzende Bereiche [1, n] und [n + 1, 2n], an den Blättern [i, i + 1]. Dabei steht [i, j] für [yi , yj ]. – c(v) := Anzahl aktiver Rechtecke, bei deren Zerlegung im Segmentbaum das Intervall [yi , yj ] entsteht. – m(v) := Maßzahl an v , Länge des Abschnittes der Sweepline von yi bis yj , die auf aktive Rechtecke trifft und bei deren Zerlegung v erreicht wird. – Die Wurzel wird für alle Rechtecke erreicht. Dort steht nach Bearbeitung von xi der Wert li . . – Seite 633/726 . – Seite 634/726 Update der m-Werte: Initialisierung: Alle c(v) = 0 und alle m(v) = 0. Situation vor dem Erreichen von x1 . – Betrachte nur Knoten, die bei Bearbeitung des aktiv/passiv werdenden Rechtecks erreicht werden. O(n) – Blatt v = [j, j + 1]: Update der c-Werte: – Ein Rechteck wird aktiv: Erhöhe c-Wert an allen Knoten, die zur Zerlegung des aktivierten Rechtecks bzgl. y -Koordinate gehören, um 1. – Ein Rechteck wird passiv: Analog, nur wird der c-Wert um 1 gesenkt. O(log n) m(v) := ( 0 , falls c(v) = 0, yj+1 − yj , falls c(v) ≥ 1. – Innerer Knoten v = [i, j]: ( m(v1 ) + m(v2 ) , falls c(v) = 0, m(v) := , falls c(v) ≥ 1. yj − y i Dabei sind v1 und v2 die Kinder von v . Updating bottom-up nach Update der c-Werte. Insgesamt O(n log n). . – Seite 635/726 O(log n) . – Seite 636/726 5.11 Analyse von Spielbäumen Wir behandeln die Basistechnik für alle Programme für Spiele wie Schach, Go, Dame, . . . Basis: Alice zieht Bob zieht – Es spielen zwei Personen: Alice und Bob. Keine Zufallsentscheidungen im Spiel (Karten mischen). Alice zieht – Vollständige Information bei Alice und Bob (keine Karten, die Alice kennt, aber Bob nicht). Bob zieht – Bei beliebigen Spielverläufen gibt es eine maximale Zugzahl, nach der das Spiel endet. (Bei Schach durch Positionswiederholungsregel erzwungen.) Alice zieht 5 8 0 1−4 4 −1 2 −2 −3 4 3 1 0 −7−1 −2 −4 1 2 3 4 2 1 −1 0 −2 −3 −4 0 0 2 – Keine Kooperation: Alice erhält, was Bob zahlt, und umgekehrt. → Spiel durch endlichen Baum beschreibbar. . – Seite 637/726 Bottom-up-Analyse . – Seite 638/726 Man muss den Gegner/die Gegnerin für ziemlich blöd halten, wenn man ein anderes Verhalten erwartet/erhofft. – An den Blättern ist alles entschieden: Spielwert s(v) = Auszahlungsbetrag. Also ist v wie ein Blatt. Wir wissen, dass Alice s(v) an Bob zahlen wird. – Innerer Knoten, bei dem alle Kinder Blätter sind: Bottom-up erhält jeder Knoten seinen Wert. Wert des Spieles := Wert an der Wurzel. v −4 10 −2 5 Betrag, den Alice an Bob zahlt Alice am Zug: Zug 1 optimal, s(v) = −4. Alice-Knoten sind min-Knoten. Optimale Strategien: Alice zieht zum Kind mit kleinstem Wert. Bob zieht zum Kind mit größtem Wert. v −4 10 −2 5 Bob am Zug: Zug 2 optimal, s(v) = 10. Bob-Knoten sind max-Knoten. . – Seite 639/726 . – Seite 640/726 0 Rechenzeit: O(Baumgröße), also linear. Das ist zu langsam. 0 −1 5 0 −1 0 −4 −1 −3 5 Man möchte gewisse Teilbäume gar nicht erst erzeugen und dabei wissen, dass keine optimale Strategie verpasst wird. 1 3 0 3 −1 1 0 −7 −4 1 3 1 3 −1 1 −1 −3 −4 Schach: Der Baum existiert nur „implizit“, er hat mehr Knoten als Atome im Weltall sind. 0 0 Praktische Programmierung: In manchen Situationen wird abgebrochen und ein Spielwert geschätzt. Daher gewinnen Computer nicht immer und verlieren sogar manchmal. . – Seite 641/726 Wenn wir manche Teilbäume mit Gewissheit abschneiden können, können wir die Rechenzeit in spannenderen Baumteilen verbringen. Vorläufige Spielwerte s∗ (v) mit folgenden Eigenschaften: – s∗ (v) . – Seite 642/726 max v min s∗ (v) = 20 vi s∗ (vi ) = 10 ⇒ s(v) ≥ s∗ (v) ≥ s∗ (vi ) ≥ s(vi ). Der wahre Wert von s(vi ) interessiert nicht. Die weitere Betrachtung von T (vi ) kann eingestellt werden. Bob geht auf keinen Fall von v zu vi . ≤ s(v) für Max-Knoten, Initialisierung: −∞. – s∗ (v) ≥ s(v) für Min-Knoten, Initialisierung: +∞. Analog: min Initialisierung erst, wenn der Knoten betrachtet wird. max v s∗ (v) = 10 vi s∗ (vi ) = 20 ⇒ s(v) ≤ s∗ (v) ≤ s∗ (vi ) ≤ s(vi ). Alice geht nicht von v zu vi . . – Seite 643/726 . – Seite 644/726 Beispiel Die Strategie heißt α-β -Pruning. v1 Alice Die anfänglichen Bob Abschneiden von Teilbäumen s∗ -Werte lassen kein Pruning zu. v2 Wie können wir ein Updating realisieren? max min v s∗ (v) = 10 vi s∗ (vi ) = 20 Alice, also Max-Knoten Bob weiß, dass er sich bei Zug 2 s(vi ) sichern kann: s∗ (v) := max{s∗ (v), s∗ (vi )}. v5 v3 v6 2 1 v7 6 1 v4 v8 8 3 v9 5 2 Bob, also Min-Knoten v10 3 4 Alice, also Max-Knoten 2 6 An min-Knoten v mit Kind vi : s∗ (v) := min{s∗ (v), s∗ (vi )}. . – Seite 645/726 . – Seite 646/726 – Linkes Kind von v8 , s∗ (v8 ) = 5, s∗ (v8 ) > s∗ (v3 ), rechtes Kind muss betrachtet werden. DFS-Traversierung (links vor rechts) – Auswertung von v2 , s(v5 ) = 2, Zug 1 an v5 optimal, s∗ (v2 ) = 2. – Linkes Kind von v6 , s∗ (v6 ) = 6. – s∗ (v6 ) ≥ s∗ (v2 ) → Pruning von T (v6 ) (nur ein Knoten nicht betrachtet, hätte aber ein riesiger Teilbaum sein können), Zug 1 an v2 optimal, s(v2 ) = 2, s∗ (v1 ) = 2. – Auswertung von v7 , s(v7 ) = 8, Zug 1 an v7 optimal, s∗ (v3 ) = 8. – Rechtes Kind von v8 , s(v8 ) = 5, Zug 1 an v8 optimal → s(v3 ) = 5, Zug 2 an v3 optimal → s∗ (v1 ) = 5. – Auswertung von v9 , s(v9 ) = 4, Zug 2 an v9 optimal, s∗ (v4 ) = 4, s∗ (v4 ) < s∗ (v1 ) → Pruning von T (v4 ) → s(v1 ) = 5, Zug 2 an v1 optimal. – Spiel vollständig analysiert. . – Seite 647/726 . – Seite 648/726 5.12 Randomisierte Suchheuristiken Wie das? Bisher: Gute Algorithmen basieren auf Strukturanalyse des Problems. Nun: Strukturanalyse nicht möglich. Warum? – Wir kennen die Menge möglicher Lösungen, können aber die zugehörigen Werte nicht analysieren: x → f (x) in einer Black Box, genauer: Experiment oder Simulation eines Experimentes. Gibt es so etwas wirklich? – Mangelnde Ressourcen (Zeit, Geld, Expertise). – In vielen Gebieten eher der Regelfall, z. B. – Optimierung technischer Systeme, – Optimierung von Biokatalysatoren, – Optimierung von Grammatiken für Phänomene verschiedener Sprachen, . . . – Kenntnisse über das Problem fehlen. . – Seite 649/726 Ziel: Randomisierte Suchstrategien auf verschiedenen Suchräumen, die bei vielen praktischen Problemen recht erfolgreich sind. Ausblick: Verknüpfung randomisierter Suchheuristiken mit teilweiser Strukturanalyse zu hybriden Algorithmen. Hier nur Grundprinzipien allgemeiner Suchheuristiken. . – Seite 650/726 Suchraum S , z. B. S = {0, 1}n (KP) S = Menge aller Permutationen auf {1, . . . , n} (TSP). f : S → R (hier zu maximieren) Wähle nach Zufallsverteilung a1 ∈ S , Information f (a1 ). Allgemein: Bekannt a1 , f (a1 ), . . . , at−1 , f (at−1 ). Wähle in Abhängigkeit von dieser Information Zufallsverteilung auf S , damit erzeuge at , Information f (at ). . – Seite 651/726 . – Seite 652/726 Selbst wenn optimale Lösung erzeugt wurde, kein Beweis dafür (im Gegensatz zu bisherigen Algorithmen). Randomisierte lokale Suche (Randomized Local Search, RLS) – Nachbarschaftsbegriff, z. B. Hammingabstand auf {0, 1}n : H(a, b) = #{i | ai 6= bi }, dann N (a) = {b | H(a, b) ≤ d}. Üblich: Informationsreduktion. Speichere nicht alle betrachteten (a, f (a))-Paare. Aktuelle Population aktiver Suchpunkte. Zusätzlich im Hintergrund: Bester bisher gefundener Suchpunkt. – Populationsgröße 1. – Initialisierung gemäß Gleichverteilung. – Aktueller Suchpunkt x, erzeuge y ∈ N (x) gemäß Gleichverteilung. Abbruchkriterium: – Anzahl erzeugter Suchpunkte. – y ersetzt x genau dann, wenn f (y) ≥ f (x). – Kein Fortschritt mehr in vielen Runden. .. . . – Seite 653/726 . – Seite 654/726 Metropolis Algorithmus – Lokale Optima können nicht verlassen werden. – Wie RLS, lokaler Suchoperator und Populationsgröße 1. – Ausweg: Multistartstrategien. – Anderer Selektionsmechanismus, um lokale Optima verlassen zu können. Eine problemspezifische Nachbarschaft beim TSP: – Temperaturparameter T ≥ 0. k -Opting: Zerschneide aktuelle Tour an k zufällig ausgewählten Stellen und setze die k Teile der Tour in zufälliger Reihenfolge zusammen. – f (y) ≥ f (x): y ersetzt x. f (y) < f (x): y ersetzt x mit Wahrscheinlichkeit e−(f (x)−f (y))/T . – T = 0: RLS, T = ∞: rein zufällige Suche. . – Seite 655/726 . – Seite 656/726 Evolutionäre Algorithmen Simulated Annealing (Analogie zur Herstellung reiner Kristalle) – Wie Metropolis Algorithmus, aber: – Sinkende Temperaturen T (t). – Z. B. T (t) = αt−1 T (1) mit α < 1 oder Temperaturabsenkung in Abhängigkeit von der Anzahl verbessernder Schritte. – Idee: Zuerst von kleinen Hügeln absteigen und höhere Berge suchen, später mehr Erklimmen des betrachteten Berges. – Globalere Suchoperatoren (Mutation), z. B. auf {0, 1}n : x → x0 Prob(x0i = xi ) = 1 − p, Prob(x0i = 1 − xi ) = p, üblicher Wert p = 1/n. H(x, x0 ) = d → Prob(x → x0 ) = pd (1 − p)n−d für p = 1/n (1/n)d (1 − 1/n)n−d ≥ (1/n)d · e−1 . W.keit, dass k Bits flippen, ungefähr k!1 · e−1 . In den meisten Schritten relativ lokale Suche, aber ab und zu große Sprünge. . – Seite 657/726 – Populationsgröße µ ≥ 1. . – Seite 658/726 – Nichtelitäre Selektion: Positive Auswahlwahrscheinlichkeit für alle, bessere Chancen bei größerer Fitness. – Innerhalb einer Generation werden λ Kinder erzeugt. – Selektion, welche Suchpunkte aus der Population Eltern werden. – Populationen neigen zur Klumpenbildung, d. h. Verlust an Diversität. – Selektion, wie aus Kindern und Eltern die Population der nächsten Generation gebildet wird. – Diversitätserhaltende Maßnahmen: – Inselmodell (sich unabhängig entwickelnde Populationen mit seltenem Individuenaustausch). – Fitness Sharing (Fitness sinkt, wenn viele andere Individuen kleinen Abstand haben). – Elitäre Selektion (survival of the fittest): Wähle die µ Individuen mit der größten Fitness (f -Wert). (µ + λ): Auswahl aus Eltern und Kindern. (µ, λ): Auswahl nur aus den Kindern. . – Seite 659/726 . – Seite 660/726 Einpunkt-Rekombination (One-point crossover) Evolutionsstrategien: Mutation als wesentlicher Suchoperator. x Genetische Algorithmen: Rekombination (Kreuzung, Crossover) als wesentlicher Suchoperator. y zufälliger Schnitt Formal: Mutation: 1 Elter, Rekombination: ≥ 2 Eltern. Gleichförmige Rekombination (Uniform crossover) x xi y yi Münzwurf, ob xi oder yi gewählt wird. . – Seite 661/726 . – Seite 662/726 Dies sind nur Grundvarianten. Alles ist erlaubt, wenn es erfolgreich ist. – Analyse bei echten Black-Box-Problemen unmöglich. Analyse randomisierter Suchheuristiken? – Analyse erhöht Intuition, bei welchem Problemtyp welche Varianten geeignet sind. – Analyse bei strukturierten Problemen möglich. – Erwartete Zeit, bis optimaler (genügend guter) Suchpunkt evaluiert wird. – Analyse unterstützt (wie stets) den Entwurf besserer Algorithmen. – Erfolgswahrscheinlichkeit, also Wahrscheinlichkeit, dass in t(n) Schritten optimaler Suchpunkt evaluiert wird. . – Seite 663/726 . – Seite 664/726 Prüfungen Zu einem algorithmischen Problem gehören – Alles aus dem Skript kann drankommen. – Problemstellung, – Vorbereitungsstrategie für jedes Unterkapitel einen Kurzvortrag einüben (∼ 4 Min.). – Motivation, „hierarchisch lernen“ – Lösungsstrategie, – die „neuen“ oder „originellen“ Ideen, – der Algorithmus, Essentials – der Korrektheitsbeweis, – die Analyse, auch der Ansatz zu den Rechnungen, aber nicht die Rechnungen, wichtige Ideen Details – die Angabe der Laufzeit. Die Schwerpunkte sind von Problem zu Problem verschieden. . – Seite 665/726 . – Seite 666/726 Kontrollstrategie – erstelle Stapel von Karteikarten mit allen Unterkapiteln / Algorithmen / Datenstrukturen / Strategien / . . . , Eine kleine Auswahl möglicher Fragen 1. Was ist der Unterschied zwischen der uniformen und logarithmischen Zeitkomplexität? – mische den Stapel, – ziehe Karteikarte zufällig, – beantworte die Frage laut und in vollständigen Sätzen und notiere, was du aufschreiben würdest, – messe die Antwortzeit, – mache dies möglichst oft mit jemandem, die oder der zuhört und möglichst etwas davon versteht. – Prüfungssituation so realistisch wie möglich simulieren . – Seite 667/726 2. Warum wird meistens die worst case und nicht die average case Rechenzeit untersucht? 3. Bei QUICKSORT gibt es dieselbe average case Rechenzeit bei Wahl des ersten Elements als Pivotelement und bei Wahl eines zufälligen Elements. Was ist der Unterschied zwischen den beiden „average cases“? . – Seite 668/726 8. Vergleiche lineare Listen und Arrays bzgl. 4. Definiere O, Ω, Θ, o, ω und warum ist ihre Verwendung sinnvoll? 5. Sortiere nach Größenordnung: 1/10 n2 log n, n2 log2 n/(log log n)10 , 2n , 2n/100 , n2 + 3n3/2 , n15/8 , n2 / log n, nlog n . – Suche nach Datum an Position n, – Suche nach Datum x, – Einfügen von y hinter x, nachdem x gefunden wurde. 9. Gib Beispiele an, bei denen es sinnvoll ist, 6. Beschreibe die Strategien lineare Suche, binäre Suche und geometrische Suche mit Vor- und Nachteilen. 7. Wie können spärlich besetzte Matrizen dargestellt werden und wie werden die Operationen wie Zugriff, Addition und Multiplikation unterstützt? – doppelte Verkettung zu haben, – Zeiger auf das letzte Element zu haben, – die Größe der Listen zu verwalten. 10. Algorithmus zum topologischen Sortieren. 11. Vergleiche Bitvektordarstellung und geordnete Listen zur Verwaltung von Mengen. . – Seite 669/726 12. Beschreibe DFS für gerichtete Graphen. . – Seite 670/726 16. Welche Operationen werden zur Verifikation von Schaltkreisen benötigt? 13. Segmentbäume 17. OBDDs – Darstellung und Minimierungsregeln. – wofür, – wie, – Rechenzeit. 18. OBDDs – Synthese mit integrierter Reduktion / Minimierung. 19. Wie sieht ein OBDD für die Addition / den Multiplexer aus? 14. UNION-FIND mit Arrays und Listen: Darstellung der Struktur, FIND, UNION, Analyse. 20. Was sind dynamische Dateien? 15. UNION-FIND mit Bäumen: Darstellung der Struktur, max. Baumhöhe bei n Daten, Pfadkomprimierung. . – Seite 671/726 21. Vergleiche offenes und geschlossenes Hashing. . – Seite 672/726 22. Beschreibe Strategien zur Kollisionsbehandlung, wieso unterscheiden sie sich in ihrem Verhalten überhaupt? 23. Was versteht man unter idealem Hashing? Wie sieht dabei der Ansatz zur Berechnung der erwarteten Suchzeit aus? 24. Was sind Suchbäume? SEARCH, INSERT, DELETE. 25. Durchschnittliche Tiefe binärer Suchbäume mit n Daten, die in zufälliger Reihenfolge kommen (Ansatz + Ergebnis asymptotisch). 26. 2-3-Bäume Definition, min. und max. Tiefe, SEARCH, INSERT, DELETE (hier wie auch sonst: Erklärung nicht nur am Beispiel, sondern strukturell, aber auch Behandlung von Beispielen möglich). 27. Das gleiche für B-Bäume. 28. Das gleiche für AVL-Bäume, insbes. Ansatz zur Berechnung der Maximaltiefe bei n Daten. Wann welcher Typ von Rotation? 29. Skiplisten, Struktur, durchschnittl. Höhe, Operationen, Analysetechnik und Anwendung. . – Seite 673/726 30. Liste die bekannten Sortieralgorithmen mit Vor- und Nachteilen auf. – warum ist die Heap Creation Phase effizienter als die Selection Phase? – Analyse der worst case Rechenzeiten der verschiedenen Strategien. 31. Quicksort – wie kommt das Zerlegungsdatum an die richtige Position? – Strategien zur Wahl des Zerlegungsdatums, – worst case, – Ansatz zur average case Analyse und Ergebnis. 33. Was ist ein allgemeines Sortierverfahren? Untere Schranke für worst und average case. 34. Bucketsort: Verfahren und Analyse. 35. Beschreibe effizienten Algorithmus für das Auswahlproblem mit Analyseansatz. 32. Heapsort – – – – . – Seite 674/726 Darstellung des Heaps im Array, das Rahmenprogramm, die reheap-Strategien, warum ist „bottom-up“ besser? 36. Batchersort: Wie geht es und warum klappt es? Sequentielle und parallele Rechenzeit. . – Seite 675/726 . – Seite 676/726 37. Beschreibe die folgenden Optimierungsstrategien allgemein: – – – – 40. Dynamische Programmierung: Was sind Probleme vom Intervalltyp? Greedy Algorithmen. Dynamische Programmierung. Divide-and-Conquer. Branch-and-Bound. 41. Berechnung statischer Suchbäume mit minimaler erwarteter Zugriffszeit. 38. Gib Beispiele für Greedy-Algorithmen an, die – stets das Optimum berechnen, – beliebig schlechte Ergebnisse berechnen können, – nicht immer optimale, aber nie schlechte Ergebnisse berechnen. 42. Was sind pseudopolynomielle Rechenzeiten? Gib ein Beispiel an. (Dyn. Progr. für das Rucksackproblem) 43. Dynamische Programmierung für das APSP. 44. Algorithmus von Dijkstra: Strategie, Korrektheit, Implementierung, Analyse, Datenstruktur. 39. Algo. von Kruskal: wozu, wie, Datenstrukturen, Rechenzeit, warum erhalten wir immer optimale Resultate? . – Seite 677/726 . – Seite 678/726 45. Die Module von Brach-and-Bound Algorithmen am Beispiel des Rucksackproblems. 50. FFT – wozu? Wie funktioniert es? 46. Divide-and-Conquer 51. Berechnung der nächsten Nachbarn in der Ebene. 52. Was ist die Sweepline-Technik? R(n) = a · R(n/b) + cn Wie wirken sich a und b auf die Rechenzeit aus? Und wie c? 47. Welche Divide-and-Conquer Algos wurden behandelt? 53. Anwendung auf das Rechteckmaßproblem, wie wird der Segmentbaum verwaltet? 54. Beschreibe das α-β -Pruning. 48. Welche Algos der dyn. Programmierung wurden behandelt? 49. Beschreibe die wesentlichen Ideen des Strassen-Algorithmus. . – Seite 679/726 . – Seite 680/726 55. Was ist das Szenario der Black-Box-Optimierung? Wie arbeiten randomisierte Suchheuristiken allgemein? 56. Beschreibe – randomisierte lokale Suche, – Simulated Annealing, – evolutionäre Algorithmen. In Klausuren können auch Beispiele zu behandeln sein! Keine Aussagen ohne Begründung! 57. Welches Ergebnis der Vorlesung hat dir warum am besten gefallen / hat dich warum am meisten überrascht? . – Seite 681/726 . – Seite 682/726 1.) Beschreibe die lineare Suche, die binäre Suche und die geometrische Suche nach einem Objekt in einem geordneten Array der Länge n. Gib die worst case Suchzeit für alle Strategien in Bezug auf n an und diskutiere die Abhängigkeit der Suchzeit von i. 2.) Für Quicksort gibt es die beiden Varianten, als Zerlegungsdatum stets das linkeste Datum oder ein Datum an zufälliger Position zu wählen. Wie groß ist die average case Rechenzeit für die beiden Varianten? Welche Variante ist warum für welche Anwendungsszenarien besser? Folgende 12 Klausuraufgaben habe ich tatsächlich in einer Klausur (Zeit 3 Stunden) gestellt. 3.) In welchem Zusammenhang kommt das Stichwort Pfadkomprimierung vor und was bedeutet es? 4.) Gib eine vollständige Definition von 2-3-Bäumen an. . – Seite 683/726 . – Seite 684/726 5.) Gegeben sei folgender AVL-Baum. 6.) Gegeben sei folgende Kostenmatrix für einen gerichteten Graph, also c(1, 3) = 6, aber c(3, 1) = 2. Wende den Algorithmus von Dijkstra zur Berechnung eines kürzesten Weges (nicht nur seiner Kosten) von Ort 1 zu Ort 5 an. Beschreibe und begründe die Zwischenschritte. Es ist nicht hilfreich, den Graphen zu zeichnen. 12 20 6 3 18 9 7 10 Führe nacheinander die Operationen INSERT(8), DELETE(18) aus. Beschreibe die angewendeten Regeln und gib die Zwischenschritte an. 1 2 3 4 5 6 7 1 0 2 6 1 10 ∞ 4 2 1 0 3 1 9 20 1 3 2 1 0 2 3 4 1 4 3 ∞ 4 0 15 10 4 5 4 0 1 2 0 3 4 6 5 0 2 1 4 0 3 7 6 3 1 1 7 5 0 . – Seite 685/726 7.) Analysiere die worst case Rechenzeit zur Erzeugung eines Heaps. Ansatz der Rechnung, Begründung des Ansatzes und Ergebnis genügen, Rechnungen selbst nicht nötig. . – Seite 686/726 11.) Wie können mit Hilfe von π -OBDDs Schaltkreise verifiziert werden und welche Operationen auf π -OBDDs werden dabei angewendet? 12.) Sei w eine primitive n-te Einheitswurzel und 8.) Warum ist die Query Time in Segmentbäumen O(log n)? 9.) Beschreibe einen Algorithmus zur Lösung des Rucksackproblems, der eine optimale Rucksackbepackung (nicht nur ihren Nutzen) mit dynamischer Programmierung berechnet und diskutiere seine Rechenzeit. f (x) = a0 + a1 x + a2 x2 + · · · + an−1 xn−1 , n = 2k ein Polynom. Beschreibe die FFT zur Berechnung von f (w0 ), f (w1 ), f (w2 ), . . . , f (wn−1 ). 10.) Beschreibe den Algorithmus Quickselect zur Lösung des Auswahlproblems und den Ansatz für die average case Analyse. . – Seite 687/726 . – Seite 688/726 5.) Insertionsort und Mergesort sortieren mit sehr wenigen wesentlichen Vergleichen. Wo liegen die Hauptprobleme bei ihrer Anwendung? Beispiele für Kurzaufgaben 1.) Definiere 2-3-Bäume. 6.) Warum stehen die untere Schranke für allgemeine Sortierverfahren und die obere Schranke für Bucketsort nicht im Widerspruch? 2.) Was ist die wesentliche Idee, wenn Daten aus dem Inneren von binären Suchbäumen/2-3-Bäumen/AVL-Bäumen entfernt werden sollen? 3.) Was unterscheidet die Durchschnittsbildung bei der Rechenzeit für das Einfügen beim geschlossenen Hashing von der Durchschnittsbildung beim Einfügen in Skiplisten? 7.) Was ist der intuitive Grund dafür, dass die lineare bottom-up Suche bei der Bottom-up-Variante von Heapsort im Durchschnitt am effizientesten ist? 4.) Erläutere das Modell des idealen Hashing (keine Rechnungen). . – Seite 689/726 . – Seite 690/726 Beispiele für Langaufgaben Szenarien für Optimierungsprobleme 1.) Analysiere die worst case Tiefe von AVL-Bäumen mit n Daten (untere Schranke, obere Schranke, Argumentation, Ansatz und Ergebnis der Rechnung genügen). Typische Optimierungsprobleme: kürzeste Wege, minimale Spannbäume, optimale Alignments, maximale Matchings, ... 2.) Was versteht man unter Rückwärtsanalyse, wo wurde sie wie angewendet? aber auch Traveling Salesperson Problem, Rucksackproblem, Scheduling, Hardwareoptimierung, ... 3.) Leite die Rekursionsgleichung für die durchschnittliche Rechenzeit von Quicksort (könnte auch Clever Quicksort sein) her. 4.) Analysiere asymptotisch die Heap Creation Phase von Heapsort. . – Seite 691/726 . – Seite 692/726 Aber in allen Fällen Ausweichmöglichkeiten: Problemstruktur bekannt, beste bekannte Algorithmen basieren auf Strukturanalyse, es sind problemspezifische Algorithmen nur für eine Aufgabe, Analyse der Algorithmen nutzt die Strukturkenntnisse. Approximationslösungen, Lösungen für Teilprobleme, randomisierte Algorithmen, teilweise kleine Fehlerwahrscheinlichkeit, ... . – Seite 693/726 Methodenreservoir immens: . – Seite 694/726 Anwendungen effizienter Algorithmen in der realen Welt oft nicht einfach Greedy Algorithmen, dynamische Programmierung, Branch and Bound, Divide and Conquer, lineare Programmierung, randomisiertes Runden, semidefinite Programmierung, ganzzahlige Programmierung, ... Reale Probleme unterscheiden sich von Lehrbuchproblemen (die aber oft den Kern der realen Probleme beinhalten) Praxisproblem Dies gilt auch für die Analysetechniken. Trade-off zwischen Effizienz des Algorithmus und Kosten und Zeit für die Entwicklung des Algorithmus (oft fehlen auch die algorithmischen Fähigkeiten) Entwurf und Analyse problemspezifischer Algorithmen – eines der ältesten und erfolgreichsten Gebiete der Informatik . – Seite 695/726 . – Seite 696/726 Daher interessant Noch schlimmer: robuste Algorithmen, Es gibt nicht immer problemspezifische Algorithmen! die (fast ohne Anpassung) viele verschiedene Probleme befriedigend effizient lösen. Wann? Black-Box Probleme Derartige Algorithmen übertreffen problemspezifische Algorithmen nicht, sind aber leichter verfügbar und anwendbar. Wir kennen die Problemdimension n und den Lösungsoder Suchraum Sn , aber nicht die zu optimierende Funktion fn : Sn → R (zumindest nicht vollständig) . – Seite 697/726 . – Seite 698/726 Wie kann man dann optimieren? Gibt es überhaupt Black-Box Probleme? Information Sampling, d.h. Das historische Beispiel: x ∈ Sn Black Box Optimierung einer Zweiphasen-Überschalldüse (Schwefel, 1968) fn (x) Maximierung des Wirkungsgrades bei der Umwandlung von thermischer in kinetische Energie (Staustrahlrohrprinzip) Experiment Simulation Optionen: Form und Länge der Düse Problem: Effekt von Verwirbelungen nicht verstanden Randomisierung praktisch unverzichtbar keine Garantie, dass Ergebnis optimal. . – Seite 699/726 . – Seite 700/726 Das moderne Beispiel: Biokatalysatoren – Evolution im Reagenzglas (Reetz, 2003) Objekte, z. B. Moleküle, heißen chiral (von Hand (griech.)), wenn sie mit ihrem Spiegelbild nicht zur Deckung gebracht werden können, z. B. Hände . – Seite 701/726 . – Seite 702/726 Chiralität und biologische Wirkung Enantiomere – chirale Moleküle die beiden Typen entstehen etwa im Verhältnis 1:1 Enantioselektive Katalysatoren sollen das Verhältnis in die gewünschte Richtung ändern → Optimierungsaufgabe . – Seite 703/726 . – Seite 704/726 Im SFB 531 behandelte Probleme Randomisierte Suchheuristiken – Temperierbohrungsstrategien für Druckguss- und Spritzgusswerkzeuge, wie Metropolisalgorithmus, Simulated Annealing, evolutionäre und genetische Algorithmen sind Antworten auf beide Problemkreise, also – Entwicklung von Prozessstrategien zur fünfachsigen Fräsbearbeitung, robuste Algorithmen und – Erhöhung der Fertigungsgenauigkeit beim Profilbiegen, Algorithmen für Black-Box Probleme – Belegungsplanung verfahrenstechnischer Mehrproduktanlagen, ... . – Seite 705/726 Wie analysiert man randomisierte Suchheuristiken? . – Seite 706/726 Analyse randomisierter Suchheuristiken ist undankbar, Beobachtung Es ist „einfacher“, maßgeschneiderte Algorithmen zu analysieren als allgemeine randomisierte Suchheuristiken, denn . . . da sie als robuste Algorithmen auf Problemen mit bekannter Struktur die besten bekannten Algorithmen nicht schlagen (kein neuer Rekord) und eine Analyse auf Problemen mit unbekannter Struktur nicht möglich ist. . . . in der Forschung hat der Algorithmenentwurf zwei Ziele: Effizienz und Nachweis der Effizienz . – Seite 707/726 . – Seite 708/726 Was macht man denn dann? – Analyse des Verhaltens auf typischen Problemen, selbst wenn es maßgeschneiderte Algorithmen gibt, um Stärken und Schwächen herauszufinden. – Herausarbeitung zentraler Fragen, um die Wirkungsweise randomisierter Suchheuristiken besser zu verstehen . . . . . . und „lehrbar“ zu machen. Wie sehen solche Fragestellungen aus? Mutation als typischer Suchoperator auf {0, 1}n , Parameter pn ( 1 − ai mit W.keit pn a0 := mut(a) a0i = ai sonst – statisch: pn konstant, meist pn = 1 n – dynamisch: festes Veränderungsschema für pn in Abhängigkeit von der Zeit – adaptiv: Veränderung gemäß Fortschritt bei der Suche – selbstadaptiv: pn ist ein Teil der Lösung und wird z. B. der Evolution unterworfen . – Seite 709/726 Untersuchte Fragen . – Seite 710/726 Weitere typische Fragestellungen – Evolutionäre Algorithmen arbeiten mit Populationen von Suchpunkten, wie lässt sich die Diversität in der Population erhalten? – Wann genügen statische Strategien? – Wann helfen die erweiterten Strategien? – Kann man entsprechende Problemeigenschaften herauskristallisieren? – Wann ist der Rekombinationsoperator (Crossover) essenziell? . – Seite 711/726 . – Seite 712/726 Simulated Annealing ist die dynamische Variante von MA, d. h. variable Temperatur T (t), hier sehr einfache dynamische Form Die Herausforderung Erstaunliche Beobachtung: Für viele Probleme ist MA bei guter Temperatur mindestens fast so gut wie jede SA-Variante. T (t) := αt−1 · T (1) mit α < 1 Gilt das immer? NEIN: – Experimente – fraktale Fitnessfunktion (Sorkin, 1991) – maßgeschneiderte Fitnessfunktion (DJW, 2001) mit den freien Parametern α und T (1). . – Seite 713/726 Jerrum und Sinclair (1996): . – Seite 714/726 Wann gilt: SA outperforms MA? It remains an outstanding open problem to exhibit a natural example in which simulated annealing with any non-trivial cooling schedule probably outperforms the Metropolis algorithm at a carefully chosen fixed value of α. α= ˆ T natural example: minimale Spannbäume (MST) – SA/MA „wissen nicht“, ob sie optimale Lösung gefunden haben – Parameter: Anzahl an Fitnessauswertungen, bis aktueller Suchpunkt optimal – erwartete Optimierzeit kann groß sein, wenn Optimum mit kleiner W.keit „verpasst“ wird und dann die Temperatur zu klein ist aber was heißt „outperforms“? . – Seite 715/726 . – Seite 716/726 Wann ist eine randomisierte Suchheuristik A erfolgreich? Wenn wir ein Problem nicht gut verstehen? Polynomielle Anzahl p(m) an Fitnessauswertungen Erfolgswahrscheinlichkeit s(m) A heißt Eine zufallsgesteuerte Suche nach besseren Lösungen nach dem Vorbild der biologischen Evolution – erfolgreich, falls s(m) ≥ 1/q(m) für ein Polynom q , – hoch erfolgreich, falls es für alle Polynome q eine polyn. Anzahl p an Rechenschritten gibt mit s(m) ≥ 1 − 1/q(m) ε – überwältigend erfolgreich, falls s(m) ≥ 1 − e−Ω(m ) für ein ε > 0, Stark vereinfacht: Evolution ist zufallsgesteuerte Optimierung in einer sich wandelnden Umwelt und Evolution ist erfolgreich (?) – erfolglos, falls s(m) = o(m−k ) für alle k und p. . – Seite 717/726 . – Seite 718/726 Beispiel Rezeptänderungen durch Mutation: Finde das Backrezept für den Lieblingskuchen (eine neue Kreation) für einen guten Freund oder eine gute Freundin – wir nennen sie Nadine. kleine oder lokale Änderungen, z. B. – etwas mehr Zucker – etwas weniger Fett Wir können nicht ausrechnen, wie Nadine ein neuer Kuchen schmeckt → – Mandelsplitter statt Schokostreusel Backen, probieren lassen, Punkte vergeben (Fitness) . – Seite 719/726 . – Seite 720/726 Neue Kreationen durch Rekombination: Die meisten Mutationen und Rekombinationen führen zu ungenießbaren Kuchen! „mische“ zwei Rezepte, Arbeite mit einer Population von Rezepten. vielleicht entsteht aus einem Rezept für eine Sachertorte und einem Rezept für eine Kirschtorte mit Sahne ein Rezept für eine Schwarzwälder Kirschtorte? Kreiere neue Rezepte, lasse nur einige neue und alte Rezepte überleben → Survival of the fittest . – Seite 721/726 Auch Schwächere (nicht besonders leckere Kuchen) können in Nischen überleben – . – Seite 722/726 Ist die Schwarzwälder Kirschtorte so entstanden ??? Verbesserung durch zeitweise Verschlechterung. Wie sonst kommt es zu der Kreation Erdbeeren mit grünem Pfeffer? Nach genügend vielen Generationen von Rezepten kann die Supertorte entstehen! . – Seite 723/726 . – Seite 724/726 Aber sollten wir so bei wichtigen technischen oder naturwiss. Problemen vorgehen? – Populationen ermöglichen die Exploration verschiedener Bereiche des Suchraumes – Selektion verstärkt die Suche in viel versprechenden Bereichen Über sehr viele Probleme wissen wir sehr viel weniger als über die Qualität von Backrezepten – – Mutation sucht meist nach lokalen Verbesserungen, aber auch größere Sprünge, um lokale Optima zu verlassen mehr als erstaunliche Erfolge mit evolutionären Algorithmen! – Rekombination ist die Hoffnung, gute Teile mittelguter Suchpunkte zu vereinen . – Seite 725/726 . – Seite 726/726