STAMMVORLESUNG EFFIZIENTE ALGORITHMEN SS 2002 Detlef Sieling Universität Dortmund Lehrstuhl Informatik 2 D-44221 Dortmund Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Jede Verwertung außerhalb der engen Grenzen des Urheberrechtsgesetzes ist ohne Zustimmung der Autoren unzulässig und strafbar. Das gilt besonders für Vervielfältigungen, Übersetzungen, Mikroverfilmungen und die Einspeicherung und Verarbeitung in elektronischen Systemen. c Ingo Wegener, Thomas Hofmeister, Paul Fischer, Detlef Sieling 2002. Das vorliegende Skript basiert zum größten Teil auf dem Skript Effiziente Algorith” men“ von Ingo Wegener von 2001, enthält aber auch Material aus Skripten von Thomas Hofmeister und Paul Fischer, bei denen ich mich für die Erlaubnis bedanken möchte, ihr Material zu verwenden. Für Verbesserungsvorschläge und die Mitteilung aufgespürter Fehler sind wir immer dankbar. Inhaltsverzeichnis 1 Übersicht 1 2 Grundlegende Graphalgorithmen 6 2.1 Depth-First Search und topologisches Sortieren . . . . . . . . . . . . . . . 6 2.2 Starke Zusammenhangskomponenten in gerichteten Graphen . . . . . . . . 8 2.3 Maximale zweifach zusammenhängende Komponenten von ungerichteten Graphen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.4 Der Algorithmus von Dijkstra zur Berechnung kürzester Wege . . . . . . . 17 3 Einige Beispiele zur Analyse von Algorithmen 20 3.1 Amortisierte Rechenzeit, Buchhaltermethode und Potentialfunktionen . . . 20 3.2 Eine Analyse der Pfadkomprimierung bei UNION-FIND Datenstrukturen . 22 3.3 String Matching – ein Mustererkennungsproblem . . . . . . . . . . . . . . . 27 4 Dynamische Programmierung 33 4.1 Ein Rückblick auf bekannte Beispiele . . . . . . . . . . . . . . . . . . . . . 33 4.2 Ein weiterer pseudopolynomieller Algorithmus für das Rucksackproblem . . 35 4.3 Bioinformatik – die Ähnlichkeit zweier Sequenzen . . . . . . . . . . . . . . 36 4.4 Hidden Markoff Modelle . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 5 Branch-and-Bound Algorithmen 43 5.1 Die Vorgehensweise bei Branch-and-Bound Algorithmen . . . . . . . . . . . 43 5.2 Ein Branch-and-Bound Algorithmus für das Rucksackproblem . . . . . . . 45 5.3 Branch-and-Bound Algorithmen für das TSP . . . . . . . . . . . . . . . . . 46 6 Approximationsalgorithmen 52 6.1 Approximationsalgorithmen für das metrische TSP . . . . . . . . . . . . . 52 6.2 Ein echt polynomielles Approximationsschema für das Rucksackproblem . . 54 6.3 Methoden zum Entwurf polynomieller Approximationsschemata . . . . . . 56 6.4 PTAS durch Glättung des Suchraums . . . . . . . . . . . . . . . . . . . . . 56 6.5 PTAS durch Glättung des Lösungsraums . . . . . . . . . . . . . . . . . . . 59 6.6 FPTAS durch Glättung der Zwischenlösungen eines langsamen Algorithmus 60 7 Lineare Optimierung, Randomisiertes Runden und Derandomisierung 63 7.1 Lineare Optimierungsprobleme . . . . . . . . . . . . . . . . . . . . . . . . . 63 i 7.2 Ein einfacher randomisierter Algorithmus für MAXSAT und MAXkSAT . . 66 7.3 Randomisiertes Runden . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 7.4 Derandomisierung mit der Methode der bedingten Wahrscheinlichkeiten . . 69 8 Randomisierte Suchheuristiken 72 8.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 8.2 Eine allgemeine obere Schranke für die erwartete Optimierungszeit des (1 + 1)EA . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 8.3 Der Einsatz von Potentialfunktionen bei der Analyse des (1 + 1)EA . . . . 77 8.4 Populationen oder Multistarts? . . . . . . . . . . . . . . . . . . . . . . . . 80 8.5 Kreuzungen können die erwartete Optimierungszeit drastisch senken . . . . 82 9 Maximierung von Flüssen in Netzwerken 87 9.1 Problemstellung und Beispiele . . . . . . . . . . . . . . . . . . . . . . . . . 87 9.2 Der Algorithmus von Ford und Fulkerson . . . . . . . . . . . . . . . . . . . 90 9.3 Der Algorithmus von Edmonds und Karp . . . . . . . . . . . . . . . . . . . 93 9.4 Der Algorithmus von Dinic . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 9.5 Ein verbesserter Algorithmus zur Sperrflussberechnung . . . . . . . . . . . 99 10 Maximale Matchings 101 10.1 Problemstellung und eine Charakterisierung optimaler Lösungen . . . . . . 101 10.2 Ein Algorithmus für bipartite Graphen . . . . . . . . . . . . . . . . . . . . 102 10.3 Blüten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 10.4 Ein Algorithmus für maximale Matchings in allgemeinen Graphen . . . . . 110 11 Algorithmen für Probleme der elementaren Zahlentheorie 114 11.1 Kryptographische Systeme . . . . . . . . . . . . . . . . . . . . . . . . . . . 114 11.2 Potenzen, multiplikative Inverse und größte gemeinsame Teiler . . . . . . . 117 11.3 Der chinesische Restklassensatz . . . . . . . . . . . . . . . . . . . . . . . . 119 11.4 Grundlagen aus der Zahlentheorie . . . . . . . . . . . . . . . . . . . . . . . 121 11.5 Das Jacobi-Symbol . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 11.6 Der probabilistische Primzahltest von Solovay und Strassen ii . . . . . . . . 126 1 Übersicht Der Entwurf effizienter Algorithmen ist unbestritten ein zentrales Teilgebiet der Informatik. Methoden zum Entwurf effizienter Algorithmen finden Anwendung in vielen Bereichen, z.B. CAD, Verifikation, Künstliche Intelligenz, Optimierung, Kryptographie, Bioinformatik. Wie entwirft man nun für ein gegebenes Problem einen effizienten Algorithmus? Notwendig sind grundlegende Kenntnisse des Bereichs, aus dem das Problem stammt. Darüber hinaus ist der Entwurf effizienter Algorithmen eine Kunst, die nicht einer erlernbaren Methode folgt. Zum Entwurf eines effizienten Algorithmus gehört also auch eine Portion Gefühl für das Problem “ und eine Portion Intuition“. Wozu dient dann eine Vorlesung ” ” Effiziente Algorithmen? Erfolgreiche Künstlerinnen und Künstler haben nicht nur die richtige Intuition, sie beherrschen darüber hinaus das benötigte Handwerk“ meisterhaft. Die Vorlesung kann und soll ” dieses Handwerkszeug vermitteln und an wichtigen Problemen erproben. Vorausgesetzt werden die Grundvorlesungen, insbesondere die Vorlesung Datenstrukturen“. Hier wird ” der Schwerpunkt nicht im Entwurf weiterer Datenstrukturen liegen, sondern in Entwurfsmethoden für Algorithmen. Dazu werden Methoden für die Bewertung der Effizienz eines Algorithmus, insbesondere für die Abschätzung der (sequentiellen) Rechenzeit, vorgestellt. Die Behandlung effizienter Algorithmen für Parallelrechner muss einer Spezialvorlesung vorbehalten bleiben. Die für diese Vorlesung ausgewählten Probleme haben viele Anwendungen in der Praxis und sind außerdem gut geeignet, um allgemeine Entwurfsmethoden für effiziente Algorithmen zu behandeln. Dabei ist zu beachten, dass einige Probleme nicht direkt in der realen ” Welt vorkommen“; Algorithmen für diese Probleme sind jedoch wichtige Unterprogramme in effizienten Algorithmen für reale Probleme. In Kapitel 2 werden grundlegende Algorithmen auf Graphen vorgestellt. DFS (Depth First Search, Tiefensuche) erweist sich als eine Routine zur Traversierung von Graphen, die für weitergehende Ziele einsetzbar ist. Neben der bekannten DFS-Nummerierung ergibt sich bei gerichteten Graphen eine topologische Nummerierung. Auf ungerichteten Graphen liefert die DFS-Routine eine Partition in die (einfachen) Zusammenhangskomponenten des Graphen. Es wird gezeigt, wie sich auch die zweifachen Zusammenhangskomponenten (die Herausnahme eine Knotens macht die Komponente nicht unzusammenhängend) mit einer erweiterten DFS-Routine berechnen lassen. Starke Zusammenhangskomponenten in gerichteten Graphen sind maximale Untergraphen, so dass es zwischen je zwei Knoten u und v sowohl einen gerichteten Weg von u nach v als auch einen gerichteten Weg von v nach u gibt. Die starken Zusammenhangskomponenten lassen sich mit zwei DFS-Durchläufen berechnen, wobei der zweite Durchlauf die Knoten in einer Reihenfolge behandelt, die sich aus dem ersten Durchlauf ergibt. Darüber hinaus wird das zentrale Problem der Berechnung kürzester Wege, das schon in der Vorlesung Datenstrukturen“ behandelt ” wurde, wiederholt. Bei den in Kapitel 2 behandelten Algorithmen ist die Analyse der Rechenzeit sehr ein1 fach, während es guter Ideen bedurfte, um die Korrektheit zu beweisen. Im Gegensatz dazu werden in Kapitel 3 Algorithmen vorgestellt, die offensichtlich die ihnen gestellte Aufgabe erfüllen. Allerdings erweist sich die Analyse der Rechenzeit als schwierig. Bei allen Algorithmen werden Unterprogramme wiederholt aufgerufen. Die Anzahl dieser Aufrufe ist ebenso leicht zu bestimmen wie die worst case Rechenzeit für einen Aufruf. Damit ist das Produkt aus Anzahl der Aufrufe und worst case Rechenzeit der Aufrufe eine obere Schranke für die Gesamtrechenzeit, allerdings in allen betrachteten Fällen eine schlechte oder zumindestens nicht genügend gute Schranke. Es ist nicht klar, welche Aufrufe viel Zeit benötigen, aber es kann gezeigt werden, dass die meisten Aufrufe wesentlich weniger Rechenzeit benötigen als die worst case Schranke angibt. Die Laufzeit über alle Aufrufe wird amortisierte Laufzeit genannt. Es werden zwei Methoden (Buchhaltermethode, Potentialfunktionen) vorgestellt, um die amortisierte Rechenzeit gut abzuschätzen. Folgende Beispielprobleme werden behandelt: UNION-FIND mit Arrays und Listen, UNION-FIND mit Pfadkomprimierung (path compression), Suchen von Mustern in Texten (string matching). Die folgenden fünf Kapitel widmen sich Methoden zum Entwurf effizienter Algorithmen. Die Methode der dynamischen Programmierung wird in Kapitel 4 untersucht. Nach einer Wiederholung der Methode an Hand der schon in der Vorlesung Datenstrukturen“ ” betrachteten Beispiele wird für das Rucksackproblem ein weiterer pseudopolynomieller Algorithmus vorgestellt. Damit haben wir dann sowohl für kleine Gewichts- wie für kleine Nutzenwerte effiziente Algorithmen für das NP-harte Rucksackproblem. Die Bioinformatik (computational molecular biology) ist nicht nur ein hochaktuelles junges Gebiet, es ist auch ein Gebiet, in dem die dynamische Programmierung besonders viele Anwendungen hat. Wir behandeln das Problem der Bewertung der Ähnlichkeit zweier Sequenzen (pairwise alignment) und die Analyse von HMMs (hidden Markoff models, Markoffketten mit verborgenen Zuständen). HMMs sind nicht nur ein zentrales Modell in der Bioinformatik, sie wurden für das Problem der automatischen Spracherkennung entwickelt und haben inzwischen vielfältige Anwendungen. Branch-and-Bound Algorithmen bilden eine Möglichkeit, NP-harte Optimierungsprobleme zu behandeln. Sie garantieren die exakte Lösung der Probleme, wozu sie allerdings exponentielle Zeit benötigen können. Sie nutzen im Laufe der Zeit die bereits erzeugten Informationen, um die Suche in schlechten Regionen des Suchraumes zu vermeiden. Damit erreichen sie in hoffentlich vielen Fällen eine gute Rechenzeit. Darüber hinaus können die Algorithmen vorzeitig abgebrochen werden. Die bis dahin beste berechnete zulässige Lösung kann als Ergebnis ausgegeben werden. Branch-and-Bound Algorithmen liefern eine Schranke für die Abweichung des Werts der erzeugten Lösung vom Optimum. Hier werden in Kapitel 5 Branch-and-Bound Algorithmen für das Rucksackproblem und das Problem des Handlungsreisenden (traveling salesman (salesperson) problem, TSP) diskutiert. Noch besser ist es natürlich, wenn wir die Güte der in polynomieller Zeit errechneten Lösung von vornherein beschränken können. Wir zeigen in Kapitel 6 zunächst, wie dies für wichtige Spezialfälle des TSP gelingt. Danach kommen wir zu polynomiellen Approximationsschemata (polynomial-time approximation schemes, PTAS) und echt (oder stark) polynomiellen Approximationsschemata (fully polynomial-time approximation schemes, 2 FPTAS) für die Lösung von Optimierungsalgorithmen. Dabei wird für vorgegebenes ε > 0 eine (1+ε)-optimale Lösung in polynomieller Zeit berechnet, wobei sich PTAS und FPTAS darin unterscheiden, wie ε die Laufzeit beeinflussen darf. Am Beispiel des Rucksackproblems zeigen wir, dass nur FPTAS wirklich effizient sind. Danach diskutieren wir Entwurfsmethoden für PTAS und FPTAS. In Kapitel 7 werden so genannte Lineare Optimierungsprobleme, manchmal auch Lineare Programme genannt, behandelt. Für lineare Optimierungsprobleme sind effiziente Algorithmen bekannt. Fügt man allerdings die Nebenbedingungen hinzu, dass nur Lösungen mit ganzzahligen Werten erlaubt sind, erhalten wir ein NP-hartes Problem, mit dem allerding viele relevante NP-harte Probleme modelliert werden können. In diesem Kapitel wird der Ansatz untersucht, zunächst das Lineare Optimierungsproblem ohne die Ganzzahligkeitsbedingung zu lösen und anschließend die möglicherweise nicht ganzzahligen Lösungen nachzubearbeiten“. Ein einfacher Ansatz hierzu besteht darin, die nicht ” ganzzahligen Lösungen mit einem randomisierten Algorithmus zu runden. Schließlich wird gezeigt, wie man den dabei enstehenden randomisierten Algorithmus derandomisieren“ ” kann, d.h., durch einen ähnlichen deterministischen Algorithmus simulieren kann. Heuristische Algorithmen spielen in Anwendungen eine große Rolle, obwohl sie weder für die Laufzeit eine polynomielle oder wenigstens pseudopoynomielle Schranke noch eine gute Qualität der Lösung garantieren. Sie bilden in vielen Fällen dennoch die beste bekannte Möglichkeit, Probleme zu behandeln. Zudem gibt es Probleme, bei denen die zu optimierende Funktion gar nicht bekannt ist. Bei technischen Systemen mit n freien Parametern ist der Einfluss bestimmter Parametereinstellungen auf die Leistung des Systems nicht berechenbar, sondern nur experimentell bestimmbar. Man spricht dann von BlackBox-Optimierung. In derartigen Situationen kommen randomisierte Suchheuristiken wie randomisierte lokale Suche, Simulated Annealing oder evolutionäre Algorithmen zum Einsatz. In Kapitel 8 soll angerissen werden, wie eine Analyse randomisierter Suchheuristiken durchzuführen ist. Wir werden uns vor allem auf evolutionäre Algorithmen konzentrieren. Die Vorlesung ist in den ersten acht Kapiteln methodenorientiert aufgebaut. Dies ist dadurch motiviert, dass wir ja Methoden einüben wollen, um sie auf weitere Probleme anwenden zu können. In den letzten drei Kapiteln sollen spezielle Probleme angesprochen werden, bei denen die Entwicklung effizienter Algorithmen besonders erfolgreich war. In Kapitel 9 behandeln wir das Problem der Berechnung maximaler Flüsse. Dieses Problem ist für sich genommen ein natürliches Optimierungsproblem, das aber auch Anwendungen bei der Lösung von anderen Optimierungsproblemen hat. Im Gegensatz zu vielen anderen zentralen Optimierungsproblemen können wir für das Flussproblem polynomielle Algorithmen entwerfen. Die Vorgehensweise dabei ist es, von einer beliebigen zulässigen Lösung auszugehen und diese schrittweise zu verbessern. Dies gelingt auf effiziente Weise, da wir einen Nachbarschaftsbegriff auf dem Raum zulässiger Lösungen definieren können, der einerseits eine effiziente Suche nach besseren Lösungen ermöglicht und andererseits sichert, dass lokal optimale Lösungen global optimal sind. Zunächst erhalten wir nur einen pseudopolynomiellen Algorithmus. Die Rechenzeit sinkt, wenn wir die Verbesserungen in einer geeigneten Reihenfolge vornehmen. Darüber hinaus können mit Hilfe von Daten- 3 strukturen viele Verbesserungen gleichzeitig“ durchgeführt werden. ” Anschließend untersuchen wir Zuordnungsprobleme, manchmal auch Matchingprobleme genannt. Wir zeigen zunächst, dass Algorithmen für Flussprobleme beim Entwurf von Algorithmen für Matchingprobleme hilfreich sind. Anschließend konstruieren wir effiziente Algorithmen ähnlich wie bei den Flussproblemen durch Verbesserung von bereits berechneten Lösungen. Kapitel 11 befasst sich mit Algorithmen für Probleme der elementaren Zahlentheorie. Die Motivation für dieses Kapitel kommt aus der Kryptographie. Moderne Kryptosysteme arbeiten mit öffentlichen Schlüsseln (public key cryptosystems). Wir stellen das RSASystem (R = Rivest, S = Shamir, A = Adleman) vor, auf dem gebräuchliche Systeme wie PGP (pretty good privacy) beruhen. Dieses System arbeitet mit sehr großen Binärzahlen (1000 Binärstellen und mehr), wobei die Arithmetik modulo einer gegebenen Zahl erfolgt. In Kapitel 11 stellen wir die effizienten Algorithmen vor, die benötigt werden, um das RSA-System effizient realisieren zu können. Das schwierigste Problem dabei ist, eine gegebene natürliche Zahl effizient darauf zu prüfen, ob sie eine Primzahl ist. Bisher gibt es dafür keine genügend effizienten deterministischen Algorithmen, wohl aber einen randomisierten Primzahltest, der stets schnell ist und nur mit sehr kleiner Wahrscheinlichkeit eine falsche Antwort gibt. Der Fehler ist einseitig, es gibt nur false positives (Zahlen, die fälschlicherweise als Primzahlen bezeichnet werden). Die Leserin und der Leser sollten im Auge behalten, dass die Erkenntnisse beim Entwurf spezieller Algorithmen für spezielle Probleme stets auch dazu dienen, allgemeine Erkenntnisse für den Entwurf effizienter Algorithmen zu gewinnen. Die Vorlesung folgt keinem Lehrbuch. Daher dient die folgende Liste von Lehrbüchern nur als Orientierung, um andere Darstellungen der hier vorgestellten Algorithmen oder weitere effiziente Algorithmen zu finden. – A.V. Aho, J.E. Hopcroft, J.D. Ullman: The design and analysis of computer algorithms, Addison-Wesley, 1974 – A.V. Aho, J.E. Hopcroft, J.D. Ullman: Data structures and algorithms, AddisonWesley, 1983 – R.K. Ahuja, T.L. Magnanti, J.B. Orlin: Network flows. Theory, algorithms and applications, Prentice-Hall, 1993 – G. Ausiello, P. Crescenzi, G. Gambosi, V. Kann, A. Marchetti-Spaccamela, M. Protasi: Complexity and approximation, Springer, 1999 – J. Bentley: Programming pearls, Addison-Wesley, 1986 – T.H. Cormen, C.E. Leiserson, R.L. Rivest: Introduction to algorithms, MIT Press, 1990 – R.L. Graham, D.E. Knuth, O. Patashnik: Concrete mathematics. A foundation for computer science, Addison-Wesley, 1989 4 – D. S. Hochbaum (Hrsg.): Approximation algorithms for NP-hard problems, PWS Publishing Company, 1997 – D.E. Knuth: The art of computer programming, Band 1-3, Addison-Wesley, 1981 – D.C. Kozen: The design and analysis of algorithms, Springer, 1991 – J.v. Leeuwen (Hrsg.): Handbook of theoretical computer science, Vol.A: Algorithms and complexity, MIT Press, 1990 – S. Martello, P. Toth: Knapsack problems, Wiley, 1990 – E.W. Mayr, H.-J. Prömel, A. Steger (Hrsg.): Lectures on Proof Verification and Approximation Algorithms, Springer, Lecture Notes in Computer Science 1367, 1998. – K. Mehlhorn: Data structures and algorithms, Band 1-3, Springer, 1984 – R. Motwani, P. Raghavan: Randomized algorithms, Cambridge Univ. Press, 1995 – T. Ottmann, P. Widmayer: Algorithmen und Datenstrukturen, BI, 1990 – C.H. Papadimitriou, K. Steiglitz: Combinatorial optimization: algorithms and complexity, Prentice-Hall, 1982 – E.M. Reingold, J. Nievergelt, N. Deo: Combinatorial algorithms: theory and practice, Prentice-Hall, 1977 – R. Sedgewick: Algorithms, Addison-Wesley, 1983 – V.V. Vazirani: Approximation algorithms, Springer, 2000 – I. Wegener: Effiziente Algorithmen für grundlegende Funktionen, Teubner, 1989 5 2 Grundlegende Graphalgorithmen Es gibt kaum ein Gebiet der Informatik, in dem nicht Graphen zur Veranschaulichung benutzt werden. Dies liegt an der Universalität“ von Graphen, paarweise Beziehun” gen zwischen Objekten darzustellen. Grundlegende Algorithmen auf ungerichteten Graphen sind somit die Basis vieler effizienter Algorithmen. Daher wiederholen wir in Kapitel 2.1 Eigenschaften der DFS-Traversierung. Insbesondere können mit dem DFS-Ansatz die Zusammenhangskomponenten in ungerichteten Graphen berechnet werden. Das Analogon für gerichtete Graphen, die so genannten starken Zusammenhangskomponenten, werden in Kapitel 2.2 mit einem doppelten DFS-Ansatz“ berechnet. Um Ausfälle von ” Komponenten kompensieren zu können, sollen Netzwerke stärker als einfach“ zusam” menhängend sein. Wir definieren in Kapitel 2.3, wann ungerichtete Graphen zweifach“ ” zusammenhängend sind und zeigen, wie mit einem modifizierten“ DFS-Ansatz die zwei” fach zusammenhängenden Komponenten eines Graphen effizient berechnet werden können. Schließlich wiederholen wir in Kapitel 2.4 Algorithmen zur Berechnung kürzester Wege in bewerteten Graphen. Die in Kapitel 2 vorgestellten Algorithmen sind nicht nur grundlegend für das ganze Gebiet Effiziente Algorithmen“. Sie sind alle einfach zu implementieren und es bereitet ” keine Schwierigkeiten, ihre Rechenzeiten zu bestimmen. Die Schwierigkeit liegt aber (mit Ausnahme von Kapitel 2.1) darin, zu zeigen, dass die Algorithmen die ihnen gestellte Aufgabe wirklich lösen, also im Korrektheitsbeweis. 2.1 Depth-First Search und topologisches Sortieren Die Idee der Tiefensuche (Depth-First Search) ist es, von einem beliebigen Knoten aus alle erreichbaren Knoten zu suchen und zu markieren. Von jedem erstmals erreichten Knoten wird direkt eine Tiefensuche gestartet. Wenn kein weiterer Knoten erreicht werden kann und noch nicht alle Knoten markiert sind, wird von einem noch nicht markierten Knoten die Tiefensuche neu gestartet. Die Suche endet, wenn von jedem Knoten aus eine Tiefensuche gestartet wurde. Bei der Tiefensuche vom Knoten v aus werden alle an v anliegenden Kanten (falls der Graph ungerichtet ist) oder alle von v ausgehenden Kanten (falls der Graph gerichtet ist) in beliebiger Reihenfolge untersucht. Charakteristikum der Tiefensuche ist, dass für jede Kante {v, w} (oder (v, w)) erst die Tiefensuche von w aus gestartet wird (falls nicht schon früher geschehen), bevor die nächste Kante {v, w 0 } (oder (v, w0 )) untersucht wird. Die DFS-Traversierung eines ungerichteten Graphen liefert folgende Informationen. Wenn ein Knoten zum ersten Mal behandelt wird, erhält er seine DFS-Nummer. Die DFSNummern werden fortlaufend vergeben und ergeben für viele Algorithmen eine hilfreiche Knotennummerierung. Zu beachten ist, dass es zu einem Graphen G = (V, E) nicht nur eine DFS-Nummerierung gibt. Wir nehmen für die DFS-Traversierung an, dass V als Liste oder Array gegeben ist und für jeden Knoten eine Adjazenzliste gegeben ist. Die DFSNummerierung hängt von der gegebenen Ordnung“ auf V und in den Adjazenzlisten ab. ” 6 Alle DFS-Nummerierungen liefern jedoch nützliche Informationen. Für jede ungerichtete Kante {v, w} wird eine Richtung ((v, w) oder (w, v)) ausgewählt, je nachdem, ob die Kante zuerst in der Adjazenzliste von v oder in der Adjazenzliste von w gefunden wurde. Wenn wir die Richtung (v, w) wählen, unterscheiden wir, ob wir die Tiefensuche von w aus bereits begonnen haben (w hat eine DFS-Nummer) oder ob wir w das erste Mal finden“. Im zweiten Fall wird (v, w) T -Kante (Treekante, Baumkante) ” und im ersten Fall B-Kante (Backkante, Rückwärtskante). Für einen zusammenhängenden Graphen bilden die T -Kanten einen Baum auf V (daher der Name), im Allgemeinen bilden sie einen Wald, dessen Teilbäume die einfachen Zusammenhangskomponenten darstellen. Während die Einteilung der Kanten in T -Kanten und B-Kanten nicht eindeutig ist, ist die Einteilung von G in Zusammenhangskomponenten eindeutig. Der ungerichtete Graph ist genau dann kreisfrei, wenn die Menge B leer bleibt. Die DFS-Traversierung betrachtet jede Kante zweimal (v ∈ Adj(w), w ∈ Adj(v)) und benötigt bei der Behandlung einer Kante konstante Zeit. Daher ist die DFS-Traversierung auf ungerichteten Graphen mit n Knoten und m Kanten in linearer Zeit O(n + m) möglich. Bei gerichteten Graphen ist die Symmetrie v ∈ Adj(w) ⇔ w ∈ Adj(v)“ aufgehoben. Wir ” könnten den Algorithmus zur DFS-Traversierung unverändert übernehmen. Die Menge der T -Kanten stellt weiterhin einen Wald dar. Da wir für gerichtete Graphen gar nicht definiert haben, wann sie zusammenhängend sind, haben die Bäume des Waldes keine Semantik. Darüber hinaus liefert uns die Einteilung in T - und B-Kanten kein notwendiges und hinreichendes Kriterium, um zu entscheiden, ob der Graph kreisfrei ist. Wir nehmen daher eine feinere Kanteneinteilung vor. Es sei (v, w) eine gerichtete Kante in G. Die von einem Knoten v aus gestartete Tiefensuche umfasst zeitlich ein Intervall I(v). In Abbildung 2.1.1 haben wir die möglichen Beziehungen zwischen I(v) und I(w) dargestellt. I(v) a) b) c) Möglichkeiten für I(w) d) e) f) Abbildung 2.1.1: Zeitlicher Verlauf der Tiefensuche. Das Besondere der Tiefensuche ist, dass die Fälle b) und d) unmöglich sind. Wird innerhalb von DFS(v) die Tiefensuche von w aus aufgerufen, wird DFS(w) vor DFS(v) beendet. Dies führt zu folgender Eingruppierung der Kante (v, w) : – w hat noch keine DFS-Nummer: (v, w) → T. 7 – w hat schon eine DFS-Nummer num(w) > num(v) : (v, w) → F (Forward-Kante, Vorwärtskante), der Knoten w ist von v aus über T -Kanten erreichbar, da DFS(w) innerhalb von DFS (v) aufgerufen wurde. – w hat schon eine DFS-Nummer num(w) < num(v), aber DFS(w) ist noch nicht beendet: (v, w) → B, der Knoten v ist von w aus über T -Kanten erreichbar, da DFS(v) innerhalb von DFS(w) aufgerufen wurde, und die Kante (v, w) schließt einen Kreis. – w hat schon eine DFS-Nummer num(w) < num(v) und DFS(w) ist bereits beendet: (v, w) → C (Cross-Kante, Querkante), der Knoten v ist von w nicht erreichbar. Wenn v und w im selben T -Baum liegen, führt die Kante (v, w) von einem später“ erzeugten Teilbaum zu einem früher“ erzeugten Teilbaum. Ansonsten ” ” führt die Kante (v, w) von einem später“ erzeugten Baum zu einem früher“ er” ” zeugten Baum. Weiterhin ist die DFS-Traversierung mit Einteilung der Kanten in linearer Zeit O(n + m) möglich. Diese Einteilung ist wiederum nicht eindeutig, aber unabhängig davon ist G genau dann kreisfrei, wenn B leer ist. Kreisfreie Graphen spielen eine besondere Rolle. Wenn Graphen eine partielle Ordnung darstellen (v ≤ w und v 6= w ⇔ (v, w) ∈ E), ist der Graph kreisfrei. In diesen Fällen sind wir an einer topologischen Nummerierung interessiert, dabei soll num(v) ≤ num(w) sein, wenn (v, w) ∈ E ist. Mit einer derartigen Nummerierung können wir die Objekte in einer Reihenfolge bearbeiten, die die gegebene partielle Ordnung respektiert. Wenn wir eine topologische Nummerierung haben, würde die zugehörige DFS-Traversierung zu einer Kanteneinteilung führen, bei der B und C beide leer sind, da Kanten nur von frühen“ Knoten zu späten“ Knoten führen. Allerdings wollen wir umgekehrt mit Hilfe ” ” des DFS-Ansatzes eine topologische Nummerierung berechnen. Dies ist folgendermaßen möglich. Wir vergeben die topologischen Nummern“ rückwärts von n bis 1. Ein Knoten ” erhält seine topologische Nummer, wenn DFS(v) beendet wird. Zur Korrektheit ist zu zeigen, dass bei Kanten (v, w) stets DFS(w) vor DFS(v) beendet wird. Wir müssen also zeigen, dass in Abbildung 2.1.1 die Fälle e) und f) unmöglich sind. Falls DFS(v) vor DFS(w) aufgerufen wird, wird DFS(w) innerhalb von DFS(v) aufgerufen, da die Kante (v, w) existiert. Dies schließt den Fall e) aus. Damit DFS(v) innerhalb DFS(w) aufgerufen werden kann, muss v von w aus auf einem gerichteten Weg erreichbar sein. Zusammen mit der Kante (v, w) hätten wir im Widerspruch zur Kreisfreiheit des Graphen einen Kreis konstruiert. Also lässt sich eine topologische Nummerierung in der Zeit O(n + m) berechnen. 2.2 Starke Zusammenhangskomponenten in gerichteten Graphen In ungerichteten Graphen hängen zwei Knoten v und w zusammen, wenn sie durch einen Weg verbunden sind. Diesen Weg können wir ja in beiden Richtungen benutzen. Daher ist es naheliegend, zwei Knoten v und w in einem gerichteten Graphen als zusammenhängend 8 zu bezeichnen, wenn es sowohl einen Weg von v zu w als auch einen Weg von w zu v gibt. Zur besseren Unterscheidung wird bei gerichteten Graphen der Ausdruck stark zusammenhängend verwendet, wenn es zwischen v und w Wege in beiden Richtungen gibt. Die Relation starker Zusammenhang (v ↔ w) ist offensichtlich eine Äquivalenzrelation: – v ↔ v (betrachte den Weg der Länge 0) – v ↔ w ⇔ w ↔ v (nach Definition) – v ↔ w, w ↔ z ⇒ v ↔ z (konkateniere die entsprechenden Wege). Damit lässt sich die Knotenmenge V in die zugehörigen Äquivalenzklassen V1 , . . . , Vk partitionieren. Die Knotenmengen V1 , . . . , Vk heißen starke Zusammenhangskomponenten des gerichteten Graphen G = (V, E). Manchmal werden auch die Teilgraphen Gi = (Vi , Ei ) mit Ei = E ∩(Vi ×Vi ) als starke Zusammenhangskomponenten bezeichnet. Es ist zu beachten, dass es Kanten zwischen den verschiedenen Komponenten geben kann (im Gegensatz zu den einfachen Zusammenhangskomponenten bei ungerichteten Graphen). Von folgendem Algorithmus wollen wir nachweisen, dass er die starken Zusammenhangskomponenten von G = (V, E) berechnet. Algorithmus 2.2.1: 1.) Führe eine DFS-Traversierung von G durch, wobei Knotennummern rückwärts von n bis 1 bei Beendigung des DFS-Aufrufs vergeben werden. 2.) Bilde den rückwärts gerichteten Graphen Gr = (V, Er ), wobei (v, w) ∈ Er genau dann ist, wenn (w, v) ∈ E ist. 3.) Führe eine DFS-Traversierung auf Gr durch, wobei die Knoten als Liste gemäß der in Schritt 1 berechneten Nummerierung vorliegen. Berechne die Knotenmengen V1 , . . . , Vk der Bäume, die durch die konstruierten T -Kanten gebildet werden. Es ist offensichtlich, dass dieser Algorithmus in Zeit O(n + m) läuft. Warum sollten aber die Knotenmengen V1 , . . . , Vk die starken Zusammenhangskomponenten darstellen? Hier hilft nur ein formaler Beweis. Satz 2.2.2: Algorithmus 2.2.1 berechnet in Zeit O(n + m) die starken Zusammenhangskomponenten von gerichteten Graphen. Beweis: Es ist zu zeigen, dass v ↔ w genau dann gilt, wenn v und w in einem T -Baum bei der zweiten DFS-Traversierung liegen. Die eine Hälfte ist leicht einzusehen. Wenn v ↔ w gilt, es also Wege von v zu w und von w zu v gibt, gilt dies auch, nachdem die Richtung aller Kanten umgedreht wurde. Wir betrachten nun die DFS-Traversierung in Schritt 3 von Algorithmus 2.2.1. O.B.d.A. wird 9 DFS(v) vor DFS(w) aufgerufen. Wenn v ∈ Vi ist, enthalten V1 , . . . , Vi−1 nur Knoten, von denen es weder Wege zu v noch Wege zu w gibt. Es sei (v, v1 , . . . , vl , w) ein Weg von v zu w. Der Algorithmus untersucht die Kante (v, v1 ). Da v1 6∈ V1 ∪ · · · ∪ Vi−1 ist, ist v1 entweder schon in Vi enthalten oder wird zu Vi hinzugefügt. In jedem Fall ist v1 ∈ Vi . Mit denselben Argumenten folgt induktiv w ∈ Vi . (Wir haben also sogar gezeigt, dass für jede DFS-Traversierung stark zusammenhängende Knoten im selben Baum landen.) Wir nehmen im Folgenden an, dass v und w bei der zweiten DFS-Traversierung im selben T -Baum landen. Sei dies der T -Baum Ti auf Vi . Wir betrachten in Ti die Wege von der Wurzel r zu v und zu w. Wir zeigen, dass v ↔ r und w ↔ r gelten. Dann folgt mit der Transitivität von ↔“ auch v ↔ w. Aus Symmetriegründen genügt es, v ↔ r zu ” beweisen. Da es in Gr einen Weg von r zu v gibt, gibt es in G einen Weg von v zu r. Nun sind wir bei dem Kern des Problems, nämlich die Existenz eines Weges von r zu v in G nachzuweisen. Als die DFS-Traversierung in Schritt 3 in Algorithmus 2.2.1 damit begonnen hat, Ti zu konstruieren, waren v und r noch nicht behandelt. Die Traversierung begann am Knoten r, der also in Schritt 1 die kleinere Nummer als v erhalten hat. Damit wurde in Schritt 1 DFS(v) vor DFS(r) beendet. Abbildung 2.1.1 (mit r an Stelle von w) gilt auch für gerichtete Graphen. Da DFS(v) vor DFS(r) beendet wurde und die Fälle b) und d) prinzipiell unmöglich sind, bleiben nur noch die Möglichkeiten e) (DFS(v) wurde beendet, bevor DFS(r) begonnen wurde) und f) (DFS(v) wurde innerhalb von DFS(r) aufgerufen). Wenn DFS(v) innerhalb DFS(r) aufgerufen wird, muss es einen Weg von r zu v in G geben (innerhalb von DFS(z) werden stets nur von z aus erreichbare Knoten behandelt). Wir beenden unsere Beweisführung, indem wir zeigen, dass es nicht möglich ist, dass DFS(v) beendet wurde, bevor DFS(r) aufgerufen wird. Wir wissen ja bereits, dass es in G einen Weg von v zu r gibt. Sei (v, v1 , . . . , vl , r) dieser Weg. Da DFS(r) nach DFS(v) aufgerufen wird, kann keiner der Knoten v1 , . . . , vl zu einem der früheren Bäume T1 , . . . , Ti−1 gehören. Damit gehören alle Knoten des Weges zu Ti (sie müssen aber nicht auf einem Weg in Ti liegen). Wenn zu Beginn von DFS(v) für keinen der Knoten v1 , . . . , vl der DFS-Aufruf bereits gestartet worden ist, wird DFS(r) innerhalb von DFS(v) aufgerufen. Dies widerspricht unserer Annahme. Also gibt es einen Knoten v ∗ auf dem betrachteten Weg, so dass DFS(v ∗ ) vor DFS(v) gestartet wurde. Damit startet DFS(v ∗ ) erst recht vor DFS(r). Da es einen Weg von v ∗ zu r gibt, endet aber DFS(v ∗ ) erst nach DFS(r). Die Situation stellt sich wie folgt dar: I(v ∗ ) I(v) I(r) Insbesondere erhält v ∗ in Schritt 1 von Algorithmus 2.2.1 eine kleinere Nummer als r. In Schritt 3 von Algorithmus 2.2.1 sind bei Beginn der Konstruktion von Ti sowohl v ∗ als auch r noch nicht erreicht. Der Algorithmus wählt unter den noch nicht erreichten Knoten einen mit kleinster Nummer, also auf keinen Fall r. Dies ist ein Widerspruch zur Annahme, dass r Wurzel von Ti ist. 2 10 Starke Zusammenhangskomponenten spielen nicht nur in typischen Graphenproblemen eine Rolle. Dazu ein Beispiel. Ein effizienter Algorithmus zur Lösung des Entscheidungsproblems 2-SAT arbeitet mit den starken Zusammenhangskomponenten eines geeignet konstruierten Graphen. 2.3 Maximale zweifach zusammenhängende Komponenten von ungerichteten Graphen Für einen ungerichteten Graphen G = (V, E) und eine Knotenmenge V 0 ⊆ V definieren wir den Teilgraphen auf V 0 als Graphen G0 = (V 0 , E 0 ), wobei E 0 genau die Kanten enthält, deren beide Knoten zu V 0 gehören. Eine andere Vorstellung ist die folgende: Wir eliminieren alle Knoten aus V − V 0 und die dazu adjazenten Kanten. Dies beschreibt die Situation in Kommunikationsnetzwerken, in denen die durch V −V 0 dargestellten Komponenten unbrauchbar geworden sind. Ein Kommunikationsnetzwerk ist gegen den Ausfall dieser Komponenten gesichert, wenn der Teilgraph auf V 0 immer noch zusammenhängend ist. Definition 2.3.1: Ein zusammenhängender ungerichteter Graph G = (V, E) heißt zweifach zusammenhängend, wenn für jeden Knoten v der Teilgraph auf V − {v} zusammenhängend ist. Ein Knoten v, für den der Teilgraph auf V −{v} nicht zusammenhängend ist, heißt Schnittpunkt von G. Der Teilgraph auf V 0 ⊆ V heißt maximale zweifach zusammenhängende Komponente, wenn er zweifach zusammenhängend ist und für jede echte Obermenge V 00 von V 0 der Teilgraph auf V 00 nicht zweifach zusammenhängend ist. Unser Ziel besteht in der Berechnung aller maximaler zweifach zusammenhängender Komponenten (ZZKs) von G = (V, E). Zunächst einmal müssen wir aber klären, ob dies eine vernünftige Aufgabe ist. Es kann exponentiell viele verschiedene optimale TSP-Rundreisen geben und auch exponentiell viele nicht vergrößerbare Cliquen. In beiden Fällen wäre es also nicht sinnvoll alle Lösungen berechnen zu wollen. Hier wird sich zeigen, dass jede Kante in genau einer ZZK liegt und wir daher eine Partition der Kantenmenge berechnen wollen. Zumindest hat die Ausgabe bezogen auf die Eingabe lineare Länge. Wir beginnen mit einer Strukturanalyse des Problems. Lemma 2.3.2: Ein Knoten, der zu zwei ZZKs gehört, ist ein Schnittpunkt. Beweis: Wir identifizieren ZZKs mit ihren Knotenmengen. Es seien V1 und V2 verschiedene ZZKs und v ∈ V1 ∩ V2 . Damit ist der Teilgraph auf V1 ∪ V2 zusammenhängend (v verbindet V1 und V2 ), aber nicht zweifach zusammenhängend. Also gibt es einen Schnittpunkt w für V1 ∪ V2 und der Teilgraph auf (V1 ∪ V2 ) − {w} ist nicht zusammenhängend, während die Teilgraphen auf V1 − {w} und V2 − {w} zusammenhängend sind, da V1 und V2 ZZKs sind. Es gibt also Knoten v1 ∈ V1 − {w}, v2 ∈ V2 − {w}, so dass der Teilgraph auf (V1 ∪ V2 ) − {w} keinen Weg zwischen v1 und v2 enthält. Falls w 6= v ist, gibt es in V1 − {w} noch einen Weg zwischen v1 und v und in V2 − {w} einen Weg zwischen v2 und v und insgesamt in (V1 ∪ V2 ) − {w} einen Weg zwischen v1 und v2 im Widerspruch zur Konstruktion von v1 und v2 . Also ist w = v und v damit Schnittpunkt. 2 11 Der Beweis von Lemma 2.3.2 zeigt auch, dass verschiedene ZZKs maximal einen gemeinsamen Schnittpunkt haben. Dieses Ergebnis halten wir als Korollar fest. Korollar 2.3.3: Zwei verschiedene ZZKs haben maximal einen Schnittpunkt gemeinsam. Lemma 2.3.4: Jede Kante gehört zu genau einer ZZK. Beweis: Sei {v, w} ∈ E. Dann ist der Teilgraph auf {v, w} zweifach zusammenhängend. Also gehört {v, w} zu einer ZZK. Es seien nun V1 und V2 verschiedene ZZKs mit v, w ∈ V1 ∩ V2 . Lemma 2.3.2 impliziert, dass v und w Schnittpunkte sind. Im Widerspruch zu Korollar 2.3.3 hätten dann die verschiedenen ZZKs V1 und V2 zwei Schnittpunkte gemeinsam. 2 Lemma 2.3.5: Schnittpunkte liegen in mindestens zwei ZZKs. Beweis: Sei v ein Schnittpunkt. Der Teilgraph auf V − {v} ist nicht zusammenhängend. Daher gibt es zwei Knoten w1 und w2 , so dass die Herausnahme von v alle Wege zwischen w1 und w2 zerstört. Wir betrachten einen Weg zwischen w1 und w2 in G. Dieser Weg führt über v und enthält zwei Kanten an v. Seien u1 und u2 die beiden Nachbarn von v auf dem Weg. Dann werden auch alle Wege zwischen u1 und u2 durch die Herausnahme von v zerstört. Die Kanten {u1 , v} und {u2 , v} liegen daher in verschiedenen ZZKs und v in zwei ZZKs. 2 Beispiel 2.3.6: 2 5 3 G 1 4 6 Abbildung 2.3.1: Welche ZZKs und Schnittpunkte hat G? G hat drei ZZKs {1, 2, 3}, {3, 4} und {4, 5, 6} und zwei Schnittpunkte 3 und 4. Wir haben gesehen, dass ZZKs an Schnittpunkten zusammenkleben“. ” Beispiel 2.3.7: Siehe hierzu Abbildung 2.3.2. In diesem Beispiel ist Z4 = ({s2 , s3 }) und Z9 = ({s5 , s6 }). Der Graph G enthält 11 ZZKs und 7 Schnittpunkte. Wie arbeitet nun ein DFS-Algorithmus auf G? Wir nehmen einmal an, dass der DFS-Algorithmus in Z7 startet. Entscheidend ist nicht, ob Schnittpunkte erreicht werden. Nach Erreichen von s1 könnte der Algorithmus trotzdem in Z7 weiter arbeiten. Entscheidend ist die Bearbeitung einer Kante aus einer anderen ZZK. Nehmen wir an, dass über s5 zum ersten Mal eine Kante (s5 , v) aus Z8 betrachtet wird. Wir benutzen hierbei die Richtung der Kante als T -Kante. Den folgenden DFSAufruf wollen wir nun als DFS(v, s5 ) bezeichnen. Er sucht von v aus, aber wir notieren uns, dass wir v von s5 aus erreicht haben. (Hat ein Knoten w in T keinen Vorgänger, 12 s1 Z2 Z1 s5 Z9 Z7 Z10 Z11 Z8 Z3 s2 Z4 s3 s7 s6 Z5 G Z6 s4 Abbildung 2.3.2: Ein Graph mit seinen ZZKs und Schnittpunkten. heißt der Aufruf DFS(w, 0).) Die Kanten von Z8 werden also nun direkt nacheinander abgearbeitet. Es wäre also schön, ein Kriterium zu haben, das uns bei Beendigung von DFS(v, s5 ) effizient sagt, dass nun die Bearbeitung einer ZZK abgeschlossen ist. Wenn wir dann nämlich alle Kanten bei ihrer ersten Betrachtung auf einem Stack ablegen, können wir den Stack abbauen, bis wir auf die Kante (s5 , v) stoßen und inklusive dieser Kante haben wir alle Kanten einer ZZK gefunden. Was passiert beim Finden der Kante (s5 , s6 )? Danach erreichen wir die erste Kante (s6 , w) von Z10 . Bevor wir den zugehörigen DFSAufruf beenden, erreichen wir Z11 über eine Kante (s7 , z). Die Kanten von Z11 werden wieder als Paket auf den Stack gelegt. Dies gilt nicht für die Kanten von Z10 . Sie werden in zwei Pakete zerlegt, da zwischendurch Z11 behandelt wird. Wenn wir aber die Kanten von Z11 als ZZK erkennen und vom Stack entfernen, schmelzen die beiden Z10 -Pakete wieder zusammen. Unser Ziel muss es also sein, für erste“ Kanten (s, a) einer ZZK am ” Ende des Aufrufes DFS(a, s) die ZZK erkannt zu haben. Es besteht die Hoffnung, dass dann alle Kanten der ZZK oben auf dem Stack liegen und (s, a) die unterste dieser Kanten ist, so dass wir alle Kanten dieser ZZK vom Stack entfernen und als ZZK in die Ausgabe schreiben können. Die erste ZZK bildet eine Ausnahme, da ihre erste“ Kante nicht einen ” Schnittpunkt berühren muss. Schließlich sollten aber am Ende die Kanten der ersten“ ” ZZK auf dem Stack stehen. Was ist das Besondere an ersten“ Kanten (s, a) einer ZZK? ” Vor dem Ende der Bearbeitung der ZZK finden wir keine Kante, die auf einen Knoten zurückzeigt, der eine kleinere DFS-Nummer als s hat. Für alle anderen Kanten (v, w 0 ) muss es wegen des zweifachen Zusammenhangs eine Kante von einem Nachfolger von v (bezüglich der DFS-Reihenfolge) zu einem Vorgänger von v geben. Dies wird sich als effizient überprüfbares Kriterium erweisen. 13 Definition 2.3.8: Sei G = (V, E) ein ungerichteter Graph und num : V → {1, . . . , n} die von der DFS-Traversierung erzeugte Knotennummerierung. Dann ist l(v) = min{num(x) | ∃ Pfad v → x aus T -Kanten und am Ende eventuell einer B-Kante}. Im folgenden Lemma werden wir sehen, dass wir mit der Kanteneinteilung des DFSAlgorithmus in T und B, mit der DFS-Nummerierung und der in l(v) enthaltenen Information Schnittpunkte erkennen können. Lemma 2.3.9: Sei G = (V, E) ein zusammenhängender, ungerichteter Graph, auf dem DF S die Nummerierung num und die Kanteneinteilung E = T ∪ B liefert. Dann sind die beiden folgenden Aussagen äquivalent. (1) a ist Schnittpunkt. (2) ∃v, w ∈ V : i) (a, v) ∈ T , ii) w 6= a, iii) w ist kein T -Nachfolger von v, iv) l(v) ≥ num(a). Beweis: (2) =⇒ (1): Da G zusammenhängend ist, bildet T einen Baum auf allen Knoten in V . Bedingung i) sagt aus, dass der DFS-Algorithmus DFS(v, a) aufruft. Wir zerlegen den T -Baum in den Teil T1 , der vor dem Aufruf von DFS(v, a) erzeugt wurde, die Kante (a, v), den Teil T2 , der während DFS(v, a) erzeugt wurde, und den Teil T3 , der nach DFS(v, a) erzeugt wurde. T2 T1 v a T3 Abbildung 2.3.3: Eine Aufteilung der T -Kanten. Die Bedingungen ii) und iii) sichern, dass es einen Knoten w 6= a in T1 oder T3 gibt. Sowohl zwischen T1 und T2 als auch zwischen T2 und T3 können wegen der Baumeigenschaft von T keine T -Kanten existieren. Der Baum T3 muss nicht an a hängen, er kann auch von T1 aus erreicht werden. Zwischen T2 und T3 gibt es auch keine B-Kante. Diese wäre innerhalb von DFS(v, a) gefunden worden, und der entsprechende Endknoten in T3 wäre Knoten in T2 geworden. Bedingung iv) sichert nun auch, dass es zwischen T2 und T1 keine B-Kante gibt. Sei y ein Knoten in T1 . Falls num(y) > num(a), ist DFS(y, ·) bereits abgearbeitet, 14 wenn die Kante (a, v) betrachtet wird. Eine Kante zwischen y und einem Knoten in T2 wäre T -Kante geworden. Falls num(y) < num(a) und die Kante {y, z} für z in T2 existiert, ist diese Kante eine B-Kante. Dann ist nach Definition 2.3.8 l(v) ≤ num(y) < num(a) im Widerspruch zu iv). Daraus folgt, dass es nach Herausnahme von a keinen Weg mehr zwischen v und w gibt. Also ist a Schnittpunkt. (1) =⇒ (2): 1.Fall: a ist Wurzel von T . Sei (a, v) die erste im DFS-Algorithmus behandelte Kante. Wenn a keinen zweiten Nachfolger in T hat, können alle anderen Knoten von v aus durch T -Kanten erreicht werden. Der Graph ist auch ohne a zusammenhängend im Widerspruch zur Voraussetzung. Sei also (a, w) ∈ T mit w 6= v. Damit sind i), ii) und iii) erfüllt. Schließlich gilt iv), da l(v) ≥ 1 = num(a) ist. 2. Fall: a ist nicht Wurzel von T . Der Schnittpunkt a kann nicht Blatt von T sein, da jeder Baum nach Herausnahme eines Blattes zusammenhängend bleibt. Also gibt es Knoten v und w mit (w, a) ∈ T und (a, v) ∈ T . Damit sind i), ii) und iii) erfüllt. Wenn a in T genau s Söhne hat, zerfällt T nach Herausnahme von a in s + 1 Zusammenhangskomponenten: Z0 mit dem Knoten w und die s Teilbäume Z1 , . . . , Zs des Baumes mit Wurzel a. Die Zusammenhangskomponenten, in die G nach Herausnahme von a zerfällt, sind Vereinigungen der Komponenten Zi . Da a Schnittpunkt ist, gibt es mindestens eine Komponente Zj , die nicht mit Z0 vereinigt wird. Bisher hatten wir Freiheiten bei der Wahl von v. Wir wählen nun v als Wurzel von Zj . Falls l(v) < num(a) ist, gibt es einen Pfad von v zum Knoten x mit l(v) = num(x), der nur aus T -Kanten und am Ende einer B-Kante besteht. Es muss x in Z0 liegen, denn alle Knoten y mit num(y) < num(a) liegen in Z0 . Da num(x) < num(a) ist, ist x 6= a. Da a aber von v aus nicht über T -Kanten erreichbar ist, führt dieser Weg nicht über a. Dieser Weg besteht also auch nach Herausnahme von a weiter. Damit sind Zj und Z0 nach Herausnahme von a noch zusammenhängend im Widerspruch zur Konstruktion. 2 Die Definition der Markierungen l(v) führt zwar zu einem polynomiellen Algorithmus zur Berechnung der l-Werte, aber nicht zu einem genügend effizienten Algorithmus. Die folgende Charakterisierung der l-Werte erleichtert diese Aufgabe. Lemma 2.3.10: l(v) = min ({num(v)} ∪ {num(x) | (v, x) ∈ B} ∪ {l(x) | (v, x) ∈ T }). Beweis: Die Menge der zur Definition von l(v) zugelassenen Wege zerfällt in drei Klassen: 1.) Den Weg der Länge 0. 2.) Wege, die aus einer B-Kante bestehen. 15 3.) Wege, die mit einer T -Kante (v, w) beginnen und mit einem zur Berechnung von l(w) zulässigen Weg fortgesetzt werden. Also beachtet die obige Formel genau alle erlaubten Wege. 2 Lemma 2.3.10 legt einen rekursiven Algorithmus zur Berechnung aller l-Werte nahe. Der folgende Algorithmus berechnet die l-Werte und gleichzeitig auch die ZZKs. Es muss dabei sichergestellt werden, dass l(v) erst dann benutzt wird, wenn es seinen richtigen Wert erhalten hat. Algorithmus 2.3.11: Berechnung der ZZKs (als Kantenmengen) eines zusammenhängenden ungerichteten Graphen G = (V, E). Dabei ist S ein Stack, der als leerer Stack initialisiert wird. Die Hilfsvariable i (Anzahl der gefundenen Knoten) startet mit dem Wert 0. Es ist für alle Knoten v der Wert num(v) mit 0 initialisiert, diese werden die DFS-Nummern werden. Die Werte l(v) kommen ohne Initialisierung aus. Am Ende des Aufrufs ZZK(w, ·) soll l(w) den Wert aus Definition 2.3.8 haben. Es wird ein beliebiger Knoten v gewählt und ZZK(v, 0) aufgerufen. Da der Algorithmus rekursiv ist, beschreiben wir den allgemeinen Aufruf ZZK(v, u), wobei für alle anderen Knoten als den Anfangsknoten (u, v) ∈ T ist. ZZK(v, u) 1.) i := i + 1; num(v) := i; l(v) := i. 2.) Für w ∈ Adj(v): 3.) if num(w) = 0 then (v, w) → S; ZZK(w, v); 4.) l(v) := min (l(v), l(w)). 5.) if l(w) ≥ num(v) then entferne Kanten vom Stack bis einschließlich (v, w). Die Kanten bilden eine ZZK und werden in die Ausgabe geschrieben. 6.) else if (num(w) < num(v) und w 6= u) then (v, w) → S; l(v) := min (l(v), num(w)) Wir analysieren Algorithmus 2.3.11. Die Nummern num(v) bilden die übliche DFSNummerierung. Alle Kanten kommen einmal auf den Stack, und zwar in gerichteter Version, entsprechend den T - und B-Kanten im DFS-Algorithmus. In Zeile 3 werden die T -Kanten und in Zeile 6 die B-Kanten auf den Stack gebracht. Alle Kanten werden auch ausgegeben und damit einer Zweizusammenhangskomponente zugeordnet. Wenn (v, w) die erste betrachtete Kante ist, ist num(v) = 1 und am Ende von ZZK(w, v) ist die Bedingung l(w) ≥ num(v) trivialerweise erfüllt und der Stack wird vollständig geleert. Am Ende von ZZK(v, u) ist l(v) korrekt berechnet. In der Initialisierung in Zeile 1 von ZZK wird num(v) berücksichtigt. Für jede B-Kante (v, w) wird in Zeile 6 num(w) berücksichtigt. Für jede T -Kante (v, w) wird in Zeile 4 l(w) berücksichtigt. Da ZZK(w, v) vor 16 ZZK(v, u) beendet ist, ist zu diesem Zeitpunkt l(w) korrekt berechnet. Also wird nach Lemma 2.3.10 auch l(v) korrekt berechnet. Wir zeigen nun, dass die ZZKs korrekt ausgegeben werden. Wir betrachten eine ZZK Z, die ein Blatt“ im Graph aller ZZKs bildet. Formaler: Wir fordern, dass V − Z zu” sammenhängend ist und dass die erste Kante nicht zu Z gehört. Es sei (v, w) die erste Kante von Z, die der DFS-Algorithmus findet. Es wird dann ZZK(w, v) aufgerufen. Da Z nur über einen Schnittpunkt erreicht werden kann, ist v ein Schnittpunkt. Von w sind alle Knoten aus Z erreichbar, darunter ist nach Annahme kein weiterer Schnittpunkt. Es werden also in Folge alle Kanten aus Z abgearbeitet. Für alle während ZZK(w, v) erreichten Knoten x gilt num(x) ≥ num(v). Also ist am Ende von ZZK(w, v) die Eigenschaft l(w) ≥ num(v) erfüllt und es wird eine Ausgabe produziert. Es bleibt zu zeigen, dass zwischen dem Auffinden von (v, w) und der Beendigung von ZZK(w, v) keine Ausgabe produziert wird. Wir nehmen an, dass am Ende von ZZK(w 0 , v 0 ) eine Ausgabe produziert wird, und folgern daraus im Widerspruch zur Annahme, dass auch v 0 ein Schnittpunkt ist. Offensichtlich ist (v 0 , w0 ) ∈ T, v 0 6= v und v kein T -Nachfolger von v 0 . Schließlich wird eine Ausgabe nur produziert, wenn l(w 0 ) ≥ num(v 0 ) ist. Nach Lemma 2.3.9 ist dann v 0 ein Schnittpunkt. Damit wird Z korrekt erkannt. Wir können nun induktiv fortfahren, indem wir Z (die zugehörige Kantenmenge) aus G entfernen. Der DFS-Algorithmus lässt dann den Aufruf von ZZK(w, v) aus. Dieses Verfahren lässt sich fortsetzen, bis nur noch eine ZZK übrigbleibt. Es sei (v, w) die erste vom Algorithmus gefundene Kante. Da num(v) = 1, wird am Ende von ZZK(w, v) eine Ausgabe produziert, obwohl v unter Umständen kein Schnittpunkt ist. Analog zu den obigen Argumenten wird während ZZK(w, v) keine Ausgabe produziert. Die Rechenzeit wird offensichtlich vom DFS-Algorithmus bestimmt und beträgt O(n+m). Satz 2.3.12: Algorithmus 2.3.11 produziert in O(n + m) Rechenschritten die maximalen zweifach zusammenhängenden Komponenten eines ungerichteten zusammenhängenden Graphen. 2.4 Der Algorithmus von Dijkstra zur Berechnung kürzester Wege Dieser Algorithmus gehört eigentlich nicht ins Hauptstudium. Die Erfahrungen bei Vordiplomprüfungen haben mich aber zu einer Wiederholung in dieser Vorlesung veranlasst. Die Aufgabe ist leicht zu beschreiben und von offensichtlicher Bedeutung. Es ist ein kantenbewerteter gerichteter Graph G = (V, E, c) gegeben, wobei c : E → + 0 eine Kostenfunktion ist (und negative Kosten ausgeschlossen sind). Für einen ausgewählten Knoten v wollen wir für alle Knoten w ∈ V die minimalen Kosten d(w) eines Weges von v zu w ebenso bestimmen wie einen Knoten N (w), der auf einem optimalen Weg von v zu w direkter Vorgänger von w ist. Auf diese Weise brauchen wir uns für jeden Knoten nur einen Knoten zu merken und können dennoch kürzeste Wege in linearer Zeit (bezogen auf die Anzahl der Knoten im kürzesten Weg) konstruieren. 17 Da es keine negativen Kosten gibt, wissen wir, dass d(v) = 0 und N (v) undefiniert ist. Unser Wissen können wir aber auch komplizierter beschreiben. Es gibt ein k, genauer k = 1, so dass wir die Menge A der k Knoten mit der kleinsten Distanz zu v kennen (wir verwenden bestimmte Artikel, obwohl mehrere Knoten denselben d-Wert haben können) und für jeden Knoten w 6∈ A die Länge dA (w) eines kürzesten Weges von v zu w kennen, wenn als Zwischenknoten nur Knoten aus A erlaubt sind. Zusätzlich kennen wir die Vorgängerknoten NA (w) auf diesen kürzesten Wegen unter der gegebenen Einschränkung der Menge der Zwischenknoten. Diese Einschränkung der Menge erlaubter Zwischenknoten kennen wir aus Algorithmen der dynamischen Programmierung. Wir stellen unser Anfangswissen dar: k = 1, A = {v}, d(v) = 0, N (v) = undef., für w 6∈ A: dA (w) = c(v, w), NA (w) = v. Für nicht existierende Kanten (v, w) setzen wir die Kosten auf ∞. Unser Ziel besteht darin, k um 1 zu erhöhen und die Menge A um einen Knoten zu ergänzen, wobei natürlich alle Parameter korrekt verändert werden müssen. Der nun folgende Ansatz ist der eines greedy Algorithmus. Es sei w ∗ ein Knoten mit minimalem dA (w)-Wert, w 6∈ A. Jeder Weg von v zu w ∗ startet in A, da v ∈ A ist, und muss irgendwann A verlassen, da w ∗ 6∈ A ist. Sei (v0 = v, v1 , . . . , vl = w∗ ) ein kostenminimaler Weg von v zu w ∗ und (vi , vi+1 ) die erste Kante mit vi ∈ A und vi+1 6∈ A. Dann betragen nach Definition unserer Parameter die Kosten des Teilweges (v0 , . . . , vi+1 ) bereits mindestens dA (vi+1 ) ≥ dA (w∗ ), wobei die Ungleichung aus der Wahl von w ∗ folgt. Also ist der minimale Weg nicht kostengünstiger als der bereits bekannte Weg von v nach w∗ , der als Zwischenknoten nur Knoten aus A benutzt. Damit können wir folgende Ergänzungen von A vornehmen: k := k + 1, d(w ∗ ) := dA (w∗ ), N (w∗ ) := NA (w∗ ), A := A ∪ {w∗ }. Nun ist aber w ∗ ein neuer erlaubter Zwischenknoten und wir müssen die dA - und NA -Werte aktualisieren. Sei also w 6∈ A. Für w gibt es nur zwei Möglichkeiten bei der Betrachtung aller Wege, deren Zwischenknoten in A liegen. Sie können w∗ ignorieren oder w ∗ benutzen. Im ersten Fall betragen die minimalen Kosten dalt A (w) (wobei wir die Bezeichnungen alt und neu nur der Klarheit wegen hinzufügen). Wenn wir w∗ benutzen, dann ist es sicher optimal mit einem optimalen Weg von v nach w ∗ zu starten, Kosten d(w ∗ ). Danach ist es nicht vernünftig, andere Knoten in A anzufassen, denn d(w∗ ) ≥ d(u) für alle u ∈ A. Damit bleibt keine Wahl mehr, es muss die Kante (w∗ , w) gewählt werden, Gesamtkosten d(w ∗ ) + c(w∗ , w). Also gilt für w 6∈ A alt ∗ ∗ dneu A (w) = min(dA (w), d(w ) + c(w , w)). alt neu alt neu ∗ ∗ Falls dneu A (w) = dA (w), ist NA (w) = NA (w). Ansonsten ist dA (w) = d(w ) + c(w , w) und wir setzen NAneu (w) = w∗ . Damit ist die Aktualisierung der Parameter abgeschlossen. Falls k = |V | ist, haben wir alle gewünschten Informationen beisammen und die Korrektheit unseres Vorgehens bewiesen. (Algorithmenbeschreibungen mit Färbungen von Knoten und Kanten sind zwar für kleine Beispiele anschaulicher, nach meinen Erfahrungen vernebeln sie aber den Blick für die Korrektheit des Vorgehens, da die Schleifeninvariante“ ” nicht deutlich wird.) 18 Wir kommen nun zur Abschätzung der Rechenzeit. Wir erhalten leicht eine O(n2 )-Schranke, da jede Vergrößerung von A in O(n) Schritten durchführbar ist: – Bestimmung von w ∗ (Minimum von maximal n Zahlen), – Aktualisierung von k, d(w ∗ ), N (w∗ ), A, – Aktualisierung aller dA (u) und NA (u), u 6∈ A. Hierbei fällt folgendes auf. Die Aktualisierung von k, d(w ∗ ), N (w∗ ) und A ist in konstanter Zeit durchführbar und es müssen nur die dA (u)- und NA (u)-Werte, u 6∈ A, aktualisiert werden, für die es die Kante (w ∗ , u) gibt. Daher ist die Rechenzeit proportional zur Anzahl der Kanten, die w ∗ verlassen. (Falls diese Anzahl Null ist, genügt konstante Zeit.) Damit lassen sich diese Aktualisierungen in Zeit O(n + m) vornehmen. Nur die Bestimmung der w∗ -Werte lässt die Rechenzeitschranke auf O(n2 ) anwachsen. Wenn die Kantenanzahl klein genug ist, lohnt es sich, gute Datenstrukturen einzusetzen. Hier sind priority queues (z.B. Heaps) angemessen. Sie verwalten Zahlen (wie die dA (u)-Werte). Das Minimum kann in Zeit O(1) bestimmt und in Zeit O(log N ) aus der Datenstruktur entfernt werden und Werte können in Zeit O(log N ) verändert werden, wenn die Datenstruktur N Werte enthält. In unserem Fall ist N ≤ n und für jede betrachtete (w ∗ , u)-Kante muss der dA (u)-Wert eventuell verändert werden. Da jede Kante nur einmal betrachtet wird, sinkt die Rechenzeit auf O(n + m log n). Der beschriebene Algorithmus ist als Algorithmus von Dijkstra bekannt. Satz 2.4.1: Auf einem bewerteten gerichteten Graphen mit nichtnegativen Bewertungen können mit dem Algorithmus von Dijkstra die kürzesten Wege von einem gegebenen Knoten zu allen anderen Knoten in Zeit O(n2 ) und auch in Zeit O(n + m log n) berechnet werden. Der Algorithmus von Dijkstra ist noch heute von großer Bedeutung. Vielleicht ist es noch wichtiger, die Informationen über kürzeste Wege zu aktualisieren, wenn sich die Bewertungen einiger Kanten geändert haben. 19 3 Einige Beispiele zur Analyse von Algorithmen Die Analyse der Rechenzeit der in Kapitel 2 vorgestellten Algorithmen war einfach. Es wurden alle Kanten des betrachteten Graphen einmal betrachtet und die Bearbeitung einer Kante hatte meistens konstante Kosten, beim Algorithmus von Dijkstra wurde eine konstante Anzahl von Operationen auf einer priority queue angestoßen. Wenn wir eine Schleife eines Algorithmus analysieren, lässt sich die Rechenzeit stets durch das Produkt aus maximaler Anzahl von Schleifendurchläufen und maximaler Zeit für einen Schleifendurchlauf abschätzen. Oft ist diese Abschätzung zumindest asymptotisch optimal. Das Ergebnis kann aber auch ganz schlecht sein. Es gibt Algorithmen, bei denen teure Schleifendurchläufe implizieren, dass (einige) andere Schleifendurchläufe billig sein müssen. In derartigen Fällen ist es viel schwieriger, zu guten Abschätzungen der Rechenzeit zu kommen. Wir wollen in Kapitel 3.1 die beiden wichtigsten Methoden zur Analyse derartiger Algorithmen an einer bekannten Implementierung von UNION-FIND Datenstrukturen vorstellen. Es folgen dann weitere Algorithmen für zentrale Probleme, die alle einfach zu implementieren sind und für die Korrektheitsbeweise auf der Hand liegen, allerdings erfordert die Analyse einige neue Ideen. In Kapitel 3.2 analysieren wir die Pfadkomprimierung (path compression) bei baumorientierten UNION-FIND Datenstrukturen. In Kapitel 3.3 betrachten wir das einfachste und grundlegendste Problem der Mustererkennung, bei dem ein Textmuster (pattern) in einem Text (string) gesucht werden soll. 3.1 Amortisierte Rechenzeit, Buchhaltermethode und Potentialfunktionen Um den Begriff der amortisierten Rechenzeit einzuführen, stellen wir uns eine Datenstruktur auf n Daten vor, die eine Operation U unterstützt. Die worst case Rechenzeit für die einmalige Durchführung von U sei t(n). Unter der amortisierten Rechenzeit T (n, m) für m Aufrufe von U verstehen wir die Rechenzeit für eine ununterbrochene Folge von m U -Befehlen. Natürlich ist T (n, m) ≤ m · t(n). Die Betrachtung der amortisierten Rechenzeit wird interessant, wenn wir in bestimmten Anwendungen stets eine größere Anzahl von Aufrufen von U haben und T (n, m) viel kleiner als m · t(n) ist. Als Beispiel betrachten wir die UNION-FIND Datenstruktur, in der wir ein Array mit den Objekten 1, . . . , n verwalten, in dem für jedes Objekt der Name der Menge steht, in der sich das Objekt momentan befindet. Außerdem verwalten wir ein Array für die Mengen (deren Namen ebenfalls aus {1, . . . , n} stammen), wobei wir für jede Menge die Größe und eine Liste aller Objekte abspeichern. Da FIND-Befehle die Datenstruktur nicht verändern, betrachten wir nur UNION-Befehle U, bei denen zwei Mengen vereinigt werden. Dies geschieht durch Auflösung der kleineren Menge und Hinzufügung der darin enthaltenen Elemente zu der größeren Menge, deren Name auch für die neue Menge übernommen wird. Außerdem werden für die Objekte der kleineren Menge die Arrayeinträge aktualisiert. Die Kosten dieser Operation sind proportional zur Größe der kleineren Menge. Wir vergessen den konstanten Faktor und nehmen als Maß die Größe der kleineren Menge. Da UNION- 20 Befehle nur für aktuelle, also nicht leere Mengen aufgerufen werden, kann eine Befehlsfolge maximal n − 1 Befehle haben. Offensichtlich kostet die Vereinigung zweier Mengen maximal n/2. Diese Kosten entstehen zum Beispiel bei der Vereinigung von {1, . . . , n/2} und {n/2 + 1, . . . , n}. Andererseits verursacht der erste Aufruf stets nur Kosten 1. Schon nach n/2 Operationen kann ein Aufruf Kosten n/4 verursachen. Wir erhalten also die obere Schranke (n − 1)n/2 und wir wissen auch, dass jeder Aufruf in der zweiten Hälfte der Operationen Kosten von mindestens n/4 verursachen kann. Hier müssen wir sorgfältig sein: jede Operation, aber nicht alle! In der Tat können wir nachweisen, dass die amortisierten Kosten Θ(n log n) sind. Eine schlechte Operationenfolge ist für n = 2k durch einen vollständig balancierten binären Baum charakterisiert, in dem die inneren Knoten die Vereinigungen symbolisieren. Wir haben dann n/2 Operationen der Kosten 1, n/4 Operationen der Kosten 2, allgemein n/2m Operationen der Kosten 2m−1 , 1 ≤ m ≤ k. Die Gesamtkosten betragen also X n n 1 · 2m−1 = k · = n log n. m 2 2 2 1≤m≤k Allerdings ist nicht klar, dass dies wirklich eine schlechteste Operationenfolge ist. Bei der Buchhaltermethode versuchen wir die Rechenschritte auf andere Kostenträger umzubuchen“. Am Ende erhalten wir als obere Schranke die Summe der bei den ver” schiedenen Kostenträgern angefallenen Kosten. Diese können wir als Produkt aus Anzahl der Kostenträger (an Stelle der Schleifendurchläufe oder Aufrufe der Operation) und der größtmöglichen Belastung eines Kostenträgers abschätzen. Im günstigsten Fall haben wir die Kosten so umgeschichtet, dass alle Kostenträger (etwa) gleich belastet sind und erhalten eine gute obere Schranke. In unserem Beispiel machen wir die Objekte zu Kostenträgern. Bei der Vereinigung von A und B mit |A| ≤ |B| fallen Kosten |A| an, die wir auf die Objekte in A verteilen, jedes Objekt in A erhält also eine Kosteneinheit. Die Anzahl der Kostenträger ist n. Wieviele Kosteneinheiten können auf ein Objekt x entfallen? Nachdem x eine Kosteneinheit erhalten hat, ist x in einer Menge, nämlich A ∪ B, die mindestens doppelt so groß ist wie die Menge A, in der x bisher war. Erhält x insgesamt t Kosteneinheiten, ist x in einer Menge mit mindestens 2t Objekten. Am Ende ist x jedoch in einer n-elementigen Menge, also muss 2t ≤ n und damit t ≤ log n sein. Da t ganzzahlig ist, ist t ≤ blog nc und wir erhalten die obere Schranke nblog nc, die nahe an der unteren Schranke ist. Bei der Methode der Potentialfunktionen messen wir den Fortschritt des Algorithmus an einer vorgegebenen Funktion. In unserem Beispiel sei für die Situation nach m UNIONBefehlen das Potential X X Φ= ϕ(i) − c(j), 1≤i≤n 1≤j≤m wobei ϕ(i) die größte Zahl t ist, so dass die das Objekt i enthaltende Menge mindestens 2t Elemente hat, und c(j) die Kosten der j-ten Operation angibt. Das Potential startet mit dem Wert 0. Bei jeder Operation wird das Potential zumindest nicht gesenkt, da für 21 alle Objekte der kleineren beteiligten Menge der ϕ-Wert um mindestens 1 steigt und die Kosten gleich der Anzahl der Objekte in der kleineren Menge sind. Also ist das Potential am Ende aller Operationen nicht negativ. Aber Φ ≥ 0 impliziert, dass die Gesamtkosten aller Operationen nicht größer als die Summe aller ϕ-Werte sein können. Die Summe aller ϕ-Werte ist aber nblog nc, da jeder ϕ-Wert genau blog nc ist. Wir erhalten also wieder die obere Schranke nblog nc. Die Buchhaltermethode und die Methode der Potentialfunktionen können nicht schematisch eingesetzt werden. Es bedarf weiterhin der richtigen Intuition, um diese Methoden so einzusetzen, dass sie zu guten Ergebnissen führen. 3.2 Eine Analyse der Pfadkomprimierung bei UNION-FIND Datenstrukturen Wir stellen nun Mengen durch Bäume dar, bei denen die Kanten in Richtung auf die Wurzel zeigen. An der Wurzel steht der Name der zugehörigen Menge und die Mächtigkeit der Menge. Für einen FIND-Befehl muss man vom Objekt x aus bis an die Wurzel des zugehörigen Baumes laufen. Die Rechenzeit ist proportional zur Weglänge. Für einen UNION-Befehl verschmelzen wir die zugehörigen Bäume, indem wir die Wurzel des ersten Baumes auf die Wurzel des zweiten Baumes zeigen lassen und dort den Namen der neuen Menge und ihre Mächtigkeit notieren. Große Bäume tendieren dazu, längere Wege zu haben als kleine Bäume. Daher wählen wir stets den Baum mit der geringeren Knotenzahl als denjenigen Baum, den wir in den anderen Baum einhängen. Jeder UNION-Befehl ist in Zeit O(1) durchführbar. Für FIND(x) ist die Länge des Weges von x zu seiner“ Wurzel ausschlaggebend. Wenn FIND(x) die Knoten x = vk , . . . , v0 ” besucht, so hat dieser Aufruf die Kosten O(k + 1). (Den Summanden +1 können wir nicht weglassen, da k = 0 möglich ist.) Wenn wir hinterher die Zeiger von vk , . . . , v1 direkt auf v0 zeigen lassen, enthält der Baum, d.h. die Menge, die gleichen Elemente wie zuvor. Für viele Knoten, d.h. Elemente, ist jedoch der Weg zur Wurzel kürzer geworden, siehe Abb. 3.2.1. Für diese Elemente sind die Kosten zukünftiger FIND-Befehle gesunken. Mit diesem Trick der Pfadkomprimierung (path compression) bauen wir für die Zukunft vor. Wir wollen zeigen, dass sich diese Vorsorge lohnt. Lemma 3.2.1: Wenn der eine Menge darstellende Baum Höhe h hat, das heißt, die Länge des längsten Weges beträgt h, dann enthält der Baum mindestens 2h Knoten. (Dies gilt auch ohne die Durchführung von Pfadkomprimierung.) Beweis: Induktion über h. Für h = 0 hat der Baum einen Knoten. Sei nun T ein Baum, der unter allen Bäumen mit Höhe h die kleinste Knotenzahl hat. T entstand durch Vereinigung der Bäume T1 und T2 , wobei o.B.d.A. size(T1 ) ≤ size(T2 ) ist. Da T1 an T2 angehängt wird, ist Höhe(T1 ) ≤ h − 1. Falls Höhe(T1 ) < h − 1, ist, da Höhe(T ) = h, Höhe(T2 ) = h. Dies ist ein Widerspruch zur Konstruktion von T , da size(T2 ) < size(T ) 22 v0 v0 v1 v1 T0 T0 v2 T1 T1 vk = x v2 T2 Tk vk = x T2 Tk Abbildung 3.2.1: Pfadkomprimierung. ist. Also gilt Höhe(T1 ) = h − 1. Nach Induktionsvoraussetzung folgt size(T1 ) ≥ 2h−1 und size(T2 ) ≥ size(T1 ) ≥ 2h−1 . Also ist size(T ) = size(T1 )+ size(T2 ) ≥ 2h . 2 Da wir n Objekte verwalten, sind die Kosten der FIND-Befehle auch ohne Pfadkomprimierung durch log n beschränkt. Allerdings sind Kosten von Θ(log n) auch möglich. Wir wollen nun erarbeiten, welche Ersparnis Pfadkomprimierung bringt. Wir analysieren den Verlauf der Datenstruktur bei Anwendung einer UNION-FINDBefehlsfolge, die n − 1 UNION-Befehle enthält. Wir gehen davon aus, dass die Befehlsfolge r Befehle enthält und schauen uns an, wie sich die Datenstruktur entwickeln würde: Einmal mit Pfadkompression, und einmal ohne Pfadkompression. Sei U0mit = U0ohne die Datenstruktur aus den n einelementigen Bäumen. Allgemein sei Uimit die Datenstruktur, die sich aus U0mit nach Abarbeitung von i Befehlen der Befehlsfolge ergibt, wenn die Pfadkompression verwendet wird. Analog für Uiohne , wenn Pfadkompression nicht verwendet wird. Da die UNION-FIND-Befehlsfolge n − 1 UNION-Befehle enthält, bestehen die Datenstrukturen Urohne und Urmit aus jeweils einem einzelnen Baum. Wir beobachten weiterhin: – FIND-Befehle ändern bei Pfadkompression zwar etwas am Aussehen des Baumes, durch den der FIND-Befehl läuft, aber a) die Knotenzahl des Baumes bleibt unverändert. b) die Wurzel des Baumes bleibt unverändert. – UNION-Befehle hängen Bäume ineinander. Die Entscheidung, welche Wurzel in die andere eingehängt wird, hängt nur von der Größe, d.h. Knotenzahl, der beiden beteiligten Bäume ab. Aus diesen beiden Beobachtungen ergibt sich: Wenn es in Uiohne einen Baum mit Wurzel i gibt, der die Menge S darstellt, so gibt es auch in Uimit einen Baum mit Wurzel i, der die Menge S darstellt, wobei allerdings beide Bäume verschieden aussehen können. 23 Die Leserin und der Leser können sich das mit den beiden obigen Beobachtungen unter Verwendung einer einfachen Induktion klarmachen. Zur Analyse der Laufzeit schaut man sich den Baum in der Datenstruktur Urohne genauer an und definiert für jedes Element den Rang des Elements über diesen Baum. Rang(x) sei die Länge des längsten Wegs von einem Blatt aus zu x im Baum Urohne . Wir beachten hierbei, dass die Zeiger im Baum von unten nach oben zeigen. x y z Im Beispielbild ist Rang(x) = 4, Rang(y) = 2, Rang(z) = 0. Der Rang eines Elements in den Datenstrukturen ist also stets fixiert und hängt nur vom Aussehen des letzten ” Baumes“ ab, d.h. des Baumes in der Datenstruktur Urohne . Auch sollte klar sein, dass im Baum der Datenstruktur Urohne die Ränge von unten nach oben wachsen“, d.h., es gilt die ” Rangwachstumseigenschaft“: Für alle v, w gilt: v Vater von w ⇒ Rang(v) > Rang(w). ” Wir wollen zunächst zeigen, dass die Rangwachstumseigenschaft in allen Bäumen gilt, die in irgendeiner der Datenstrukturen Uimit vorkommen. Wir betonen dabei noch einmal, dass der Rang immer über den Baum in der Datenstruktur Urohne definiert definiert ist, auch wenn wir andere Bäume betrachten. Lemma 3.2.2: Sei i beliebig. Wenn in der Datenstruktur Uimit das Element v Vater von Element w ist, dann gilt Rang(v) > Rang(w). Beweis: Zu Beginn, für i = 0, liegen nur n einelementige Mengen vor, dort ist kein Knoten Vater eines anderen, also ist die Aussage sicherlich erfüllt. Nehmen wir nun an, dass es ein i gibt, so dass in Uimit die Rangwachstumseigenschaft“ nicht erfüllt ist. Sei i ” mit so gewählt, dass die Rangwachstumseigenschaft“ in den Datenstrukturen U0mit , . . . , Ui−1 ” mit gilt, aber in Uimit nicht mehr. In Ui−1 gilt die Rangwachstumseigenschaft also noch. Wenn wir nun einen FIND-Befehl abarbeiten würden, so würde eine neue Datenstruktur entstehen, in der immer noch die Rangwachstumseigenschaft gilt, denn es werden ja nur Knoten weiter nach oben (d.h., an die Wurzel) gehängt, und dort ist der Rang noch höher (denn mit in Ui−1 galt die Rangwachstumseigenschaft noch.) mit Also muss Uimit aus Ui−1 durch einen UNION-Befehl entstanden sein. Nehmen wir an, dass der Baum mit der Wurzel t2 in den Baum mit der Wurzel t1 eingehängt wird. Die einzige neue Vater-Sohn-Beziehung im entstehenden Baum ist die zwischen den beiden Elementen t1 und t2 . Da in Uimit die Rangwachstumseigenschaft verletzt ist, muss also 24 Rang(t1 ) < Rang(t2 ) gelten. Andererseits haben unsere Überlegungen vorhin gezeigt, dass in der Folge Ujohne die gleichen Wurzeln ineinandergehängt werden, also entsteht ohne auch Uiohne aus Ui−1 durch Einhängen eines Baumes mit Wurzel t2 in einen Baum mit Wurzel t1 . In der Datenstrukturfolge ohne Pfadkompression jedoch bleiben Vater-SohnRelationen erhalten, das heißt, wenn einmal Knoten v Vater von Knoten w ist, so bleibt v Vater von w. Das heißt aber auch, dass im Baum Urohne das Element t1 immer noch Vater von Element t2 ist, also nach Definition des Ranges ist Rang(t1 ) ≥ Rang(t2 ), ein Widerspruch zur Annahme, dass die Rangwachstumseigenschaft verletzt ist. 2 Wir beweisen noch eine weitere Eigenschaft des Ranges. Lemma 3.2.3: Es gibt höchstens n/2r Elemente mit Rang r. Beweis: Nach Lemma 3.2.1 hat der Baum mit Wurzel x, wenn Rang(x) = r ist, mindestens 2r Knoten. Kein Element mit Rang r kann Vorgänger oder Nachfolger (in W ) eines anderen Elements mit Rang r sein. Gäbe es mehr als n/2r Elemente mit Rang r, müsste es also mehr als n Elemente geben. 2 Definition 3.2.4: Es sei F (0) := 1 und F (i) := 2F (i−1) sowie log∗ n := min{k | F (k) ≥ n}. F wächst wahnsinnig schnell“, log∗ wächst furchbar langsam“, ist aber nicht konstant. ” ” i 0 1 2 3 4 ··· ∗ log i 0 0 1 2 2 · · · 0 1 2 3 4 5 i 65536 F (i) 1 2 4 16 65536 2 100000 5 F (k) ist ein Turm aus k Zweien. Es ist log∗ n ≤ 5 für alle n ≤ 265536 . Man kann log∗ n auch anders interpretieren: log∗ n gibt an, wie oft man n logarithmieren muss, um eine ” Zahl a ≤ 1 zu erhalten.“ Wir können nicht verhindern, dass einzelne FIND-Befehle Kosten von Θ(log n) verursachen. Wir wollen aber zeigen, dass in einer UNION-FIND-Befehlsfolge mit n − 1 UNIONBefehlen und m FIND-Befehlen die Kosten der FIND-Befehle insgesamt nur O((m + n) · log∗ n) betragen. Dazu wollen wir die Buchhaltermethode verwenden. Kosten, die anfallen, werden auf verschiedenen Kostenkonten verbucht. Schließlich analysieren wir, was auf diesen einzelnen Konten höchstens verbucht werden kann. Wir haben den Knoten, also den Objekten, Ränge zugeordnet. Verschiedene Ränge werden nun zu Ranggruppen zusammengefasst. Ein Knoten mit Rang r wird der Gruppe mit Nummer log∗ r zugeordnet. Also bilden z.B. die Knoten mit Rang r ∈ {5, . . . , 16} die Ranggruppe 3. Diese Wahl der Ranggruppen wird sich bei der folgenden Rechnung als gut“ erweisen. Knoten mit Rang 0 gehören der Ranggruppe 0 an. Anschaulich sieht das ” folgendermaßen aus: Sei nun FIND(x) ein Befehl, dessen Weg zur Wurzel der Weg x = vk → vk−1 → · · · → v0 ist. Welche Kostenkonten verwenden wir? Für jeden FIND-Befehl steht ein Konto zur Verfügung und für jedes Element 1, . . . , n steht ein Konto zur Verfügung. Für den Befehl 25 F(3) Rang 16 Ranggruppe 3 F(2) Rang 4 Rang 3 F(1) Rang 2 Rang 1 Rang 0 Ranggruppe 2 Ranggruppe 1 Ranggruppe 0 FIND(x) haben wir k + 1 Kosteneinheiten zu verteilen. Eine Einheit dafür, dass der Befehl aufgerufen wurde sowie eine Einheit für jede betrachtete Kante. Wir verteilen nun diese k+1 Kosten wie folgt auf die Konten: Eine Einheit kommt in das Konto für den FIND-Befehl dafür, dass der Befehl aufgerufen wurde. Eine Einheit für eine Kante v1 → v0 kommt auf das Konto für den FIND-Befehl. Für die anderen Kanten vi → vi−1 hängt die Wahl des Kontos von den Ranggruppen der beteiligten Elemente ab: – Wenn vi in einer anderen Ranggruppe als vi−1 ist, verbuche Kosten 1 auf dem Konto des FIND-Befehls. – Wenn vi in der gleichen Ranggruppe wie vi−1 ist, verbuche Kosten 1 auf dem Konto des Elements vi . Es ist klar, dass wir die Gesamtkosten erhalten, wenn wir die angelaufenen Kosten in allen Konten aufaddieren. Schauen wir uns zunächst das Konto eines FIND-Befehls an: Dort können nur log∗ n + 2 viele Kosteneinheiten verbucht werden: maximal zwei für die Kante v1 → v0 und für den Aufruf. Da es höchstens 1 + log∗ n Ranggruppen gibt, können wir beim Hochlaufen eines Pfades nur log∗ n mal die Ranggruppe wechseln. In den Konten aller m FIND-Befehle zusammen genommen können also höchstens m(log∗ n+2) = O(m·log∗ n) Kosteneinheiten verbucht werden. Nun kommen wir zu den Konten für die Elemente 1 bis n. Wenn ein Element bei unserer Kostenaufteilung eine Kosteneinheit erhalten hat, dann bekommt es durch die Pfadkompression einen neuen Vater, nämlich v0 . Der Rang dieses Vaters ist wegen der Rangwachstumseigenschaft also um mindestens 1 größer als der Rang des vorigen Vaters. Wir betrachten ein Element in Ranggruppe g, sein Rang ist also in {F (g − 1) + 1, . . . , F (g)}. Spätestens nachdem dieses Element F (g) − F (g − 1) Kosteneinheiten zugeteilt bekommen hat, liegt der Vater in einer höheren Ranggruppe. Danach wird diesem Element keine weitere Kosteneinheit zugeordnet. Jedes Element in Ranggruppe g erhält also höchstens F (g) − F (g − 1) ≤ F (g) Kosteneinheiten zugeteilt. Es sei N (g) die Zahl der Elemente in 26 Ranggruppe g, also die Zahl der Elemente mit Rang in {F (g − 1) + 1, . . . , F (g)}. Nach Lemma 3.2.3 ist N (g) ≤ X F (g−1)+1≤r≤F (g) n2 −r ≤ n2 −F (g−1) 1 1 + + ··· 2 4 = n2−F (g−1) = n/F (g), da 2F (g−1) = F (g) ist. Die Gesamtzahl der Kosteneinheiten, die Elemente aus Ranggruppe g erhalten, ist durch N (g)F (g) ≤ n beschränkt. Da nur log∗ n + 1 Ranggruppen betrachtet werden, ist die Gesamtzahl der Kosteneinheiten für die Elemente höchstens n·(log∗ n+1). Zusammenfassend erhalten wir folgenden Satz. Satz 3.2.5: Für die UNION-FIND-Datenstruktur mit Bäumen und Pfadkompression gilt: Die n−1 UNION-Befehle und m FIND-Befehle können in Zeit O((n + m) · log∗ n) durchgeführt werden. Wir fassen unsere Analysen über UNION-FIND Datenstrukturen zusammen. Es sei m die Anzahl der FIND-Befehle und n die Anzahl der Objekte. Dann ist die Anzahl der UNION-Befehle maximal n − 1. – Für die arrayorientierte Datenstruktur (siehe Kapitel 3.1) beträgt die Rechenzeit O(n log n + m). – Für die baumorientierte Datenstruktur mit Pfadkomprimierung beträgt die Rechenzeit O(n log∗ n + m log∗ n). Tatsächlich kann für die zweite Datenstruktur die Rechenzeit asymptotisch exakt bestimmt werden. Sie beträgt Θ((n + m)α(n, m)), wobei α die Inverse der AckermannFunktion ist, eine Funktion, die noch schneller als die Zweierturm-Funktion wächst. Die asymptotisch korrekte Rechenzeit eines sehr einfachen Algorithmus lässt sich also nur durch eine äußerst komplizierte Funktion beschreiben. Die Rechenzeit bei Pfadkomprimierung ist also fast linear“, aber eben nicht linear. ” 3.3 String Matching – ein Mustererkennungsproblem Wir wollen hier das einfachste Problem aus dem Gebiet Mustererkennung diskutieren. Die Aufgabe besteht darin, einen String S = (s1 , . . . , sn ) darauf zu untersuchen, ob er ein Muster (Pattern) P = (p1 , . . . , pm ) als Teilstring enthält. Das Problem hat eine einfache Lösung. Teste für i ∈ {0, . . . , n − m}, ob für alle j ∈ {1, . . . , m} die Beziehung pj = si+j gilt. Also genügen O(nm) Rechenschritte. Diese worst case Rechenzeit wird zum Beispiel für S = (a, a, . . . , a) und P = (a, a, . . . , a, b) erreicht. Es sei Σ das zugrundeliegende Alphabet. Unser Mustererkennungsproblem ist das Wortproblem für die reguläre Sprache L = Σ∗ p1 · · · pm Σ∗ . Der nichtdeterministische endliche Automat A (NFA) in Abbildung 3.3.1 erkennt gerade diese Sprache. 27 Σ Σ NFA A p1 p2 pm Anfangszustand akzeptierender Zustand Abbildung 3.3.1: Ein NFA zur Mustererkennung. Wir haben damit einen nichtdeterministischen Algorithmus, der in n Schritten entscheidet, ob S ∈ L ist. Nichtdeterministische Algorithmen haben keine direkte praktische Bedeutung. Wir können mit der Potenzmengenkonstruktion aus GTI aus dem NFA A einen DFA A0 für die Sprache L konstruieren. Mit Hilfe von A0 kann unser Mustererkennungsproblem in n Schritten entschieden werden. Allerdings kann die Preprocessing Zeit, nämlich die Zeit zur Konstruktion von A0 , erheblich sein. Es kann nicht ausgeschlossen werden, dass A0 exponentiell (in m) viele Zustände benötigt. Während die oben geschilderte naive Methode ohne Preprocessing auskommt, aber eine Bearbeitungszeit von O(nm) hat, ist hier die optimale Bearbeitungszeit von n erreicht – auf Kosten der Preprocessing Zeit. Knuth, Morris und Pratt haben einen guten Kompromiss gefunden. Nach einer Preprocessing Zeit von O(m) genügt eine Bearbeitungszeit von O(n). Ihr Algorithmus ist auch als KMP-Algorithmus bekannt. Um die im Preprocessing Schritt zu erledigende Aufgabe zu spezifizieren, stellen wir das Ziel vor. Bei der Bearbeitung des Strings wollen wir nach dem Lesen des i-ten Buchstabens wissen, ob (s1 , . . . , si ) bereits das Muster enthält. Ansonsten wollen wir den längsten Suffix von (s1 , . . . , si ) kennen, der auch Präfix des Musters ist. Dazu wollen wir einen Graphen auf V = {0, . . . , m} verwenden. Der Zustand j ≥ 1 soll die größte Zahl bezeichnen, so dass (si−j+2 , . . . , si ) = (p1 , . . . , pj−1 ) ist. Wenn wir den nicht vorhandenen Knoten m + 1 erreichen würden, haben wir das Muster im String gefunden. Der Knoten 0 ist nur ein Hilfsknoten zur einfacheren Beschreibung des späteren Algorithmus. Wenn das Muster nicht gefunden wurde, lesen wir si+1 . Das Problem besteht in der Berechnung des neuen j-Wertes. Der Graph auf V soll in der Preprocessing Phase Kanten erhalten, die uns helfen, die Berechnung des neuen j-Wertes durchzuführen. Jeder Knoten j ≥ 1 wird eine ausgehende Kante erhalten. Wir diskutieren unser Vorgehen zunächst an einem Beispiel. 28 P : EINMALEINENEFFIZIENTENALGORITHMUSENTWERFEN S1 : ESWAREINMALEINEHEXE... S2 : ESWAREINMALEINMENSCH... S3 : ESWAREINMALEINRIESE... Wenn wir im String ESWAREINMALEIN gelesen haben, ist der längste Suffix, der Präfix von P ist, EINMALEIN und besteht aus 9 Buchstaben. Wir wollen dann im Knoten 10 sein. Der nächste zu lesende Stringbuchstabe muss mit pj verglichen werden, im Beispiel mit p10 = E. Bei Übereinstimmung erreichen wir den Knoten j + 1. Falls j = m, kann die Suche erfolgreich abgebrochen werden. Für S1 erreichen wir Knoten 11. Bei S2 und S3 gibt es keine Übereinstimmung. Wir wollen aber nun die Suche nicht wie im trivialen Algorithmus von vorn beginnen. Suffixe von S, die Präfixe von P sind, heißen gute Suffixe. Es hat sich gezeigt, dass der längste gute Suffix mit dem nächsten Stringbuchstaben zusammen keinen guten Suffix bildet. Die naheliegende Idee ist es, den zweitlängsten guten Suffix, dann den drittlängsten guten Suffix, . . . zu testen, ob er mit dem nächsten Stringbuchstaben zu einem guten Suffix wird. Es ist nun wichtig zu erkennen, dass wir vom String nur den längsten Teil (si−j+2 , . . . , si ) betrachten, der auch Präfix von P ist. Also können wir alle Betrachtungen unabhängig von dem String S machen. Wir haben also einen Präfix P 0 = (p1 , . . . , pj−1 ) von P und suchen den längsten echten Suffix P 00 von P 0 , der Präfix von P ist. Gesucht ist also das größte k < j, so dass (p1 , . . . , pk−1 ) = (pj−k+1 , . . . , pj−1 ) ist. In unserem Beispiel ist k = 4. Der Suffix EIN von EINMALEIN ist der längste echte Suffix, der auch Präfix ist. Jetzt versuchen wir wieder, den nächsten Stringbuchstaben zu verarbeiten. Da wir im Knoten 4 des Diagramms sind, muss der gelesene Buchstabe mit p4 = M verglichen werden. In S2 sind wir erfolgreich und erreichen Knoten 5. EINM ist nun der längste gute Suffix in dem gelesenen Teil von S2 . In S3 sind wir nicht erfolgreich. In EIN gibt es keinen echten Suffix, der auch Präfix ist. Also ist k = 1. Im Knoten 1 vergleichen wir den gelesenen Buchstaben mit p1 = E. Auch dies ist nicht erfolgreich. In jedem Fall können wir das Lesen des 15-ten Stringbuchstabens beenden. Für S1 sind wir im Knoten 11, für S2 im Knoten 5 und für S3 im Knoten 1. Der Knoten 0 soll ein Hilfsknoten werden, in dem wir erkennen, dass für den gelesenen Stringbuchstaben wieder in Knoten 1 gestartet werden muss. Die Informationen, die wir jetzt diskutiert haben, sind nötig für den Fall, dass der längste gute Suffix nicht ausgebaut werden kann. Daher heißen sie F -Kanten oder Fehlerkanten. Definition 3.3.1: F (j) := max {0 ≤ k ≤ j − 1 | (p1 , . . . , pk−1 ) = (pj−k+1 , . . . , pj−1 )}. Da leere Vektoren übereinstimmen, ist die Bedingung für k = 0 und k = 1 stets erfüllt. Daher ist für j ≥ 2 stets F (j) ≥ 1, F (2) = 1 und F (1) = 0. Außerdem ist F (j) < j. Es wird also stets der nächstkleinere gute Suffix aufgesucht und probiert, ob er verlängert werden kann. Im Knoten 1 wird der leere Suffix getestet, ob er verlängert werden kann. Nur, wenn auch das nicht möglich ist, wird Knoten 0 durch die Fehlerkante aufgesucht. Am Ende suchen wir den Nachfolgeknoten auf, damit unsere Interpretation des Knotens 29 j wieder stimmt. Wir geben zunächst den Algorithmus an, der mit Hilfe der Fehlerkanten das String Matching Problem löst. Hinterher zeigen wir, wie die Fehlerkanten effizient berechnet werden können. Algorithmus 3.3.2: Input: n; m; P = (p1 , . . . , pm ) ∈ Σm ; S = (s1 , . . . , sn ) ∈ Σn ; F (1), . . . , F (m), die nach Definition 3.3.1 gebildeten Fehlerkanten. Output: Erfolg oder kein Erfolg, abhängig davon, ob P in S enthalten ist. 1.) i := 1; j := 1. 2.) Solange i ≤ n, führe die Schritte 3, 4 und 5 aus. 3.) Solange j 6= 0 und pj 6= si setze j := F (j). 4.) Falls j = m, Output Erfolg“ und STOP. ” Falls j 6= m, setze i := i + 1; j := j + 1. 5.) 6.) Output kein Erfolg“ und STOP. ” Satz 3.3.3: Algorithmus 3.3.2 löst das String Matching Problem in O(n) Rechenschritten. Beweis: Der Parameter i (die im String erreichte Position) wird nur erhöht. Er startet mit dem Wert 1 und beim Wert n + 1 stoppt der Algorithmus. Also wird Schritt 5 höchstens n-mal aufgerufen. Schritt 5 ist der einzige Schritt, in dem j erhöht wird. Also wird j höchstens n-mal um jeweils 1 erhöht. Wenn Schritt 3 ausgeführt wird, sinkt j um mindestens 1. Wenn Schritt 3 mindestens (n + 2)-mal ausgeführt wird, ist j negativ (da j mit dem Wert 1 startet). Andererseits kann j nicht negativ werden. Also wird Schritt 3 höchstens (n + 1)-mal durchgeführt. Damit ist die Rechenzeit O(n). 2 Implizit haben wir hier j als Potentialfunktion verwendet. Wir haben es in Schritt 3 mit einer Schleife zu tun, die n-mal neu gestartet werden kann (zum Beispiel, wenn String und Muster nur verschiedene Buchstaben enthalten). Es kann aber auch passieren, dass ein Start der Schleife zu Ω(m) Befehlen führt (Muster (ab)m/2 und der String beginnt mit (ab)m/2−1 aa). Das Potential j startet bei 1, wird nur höchstens n-mal um 1 erhöht, und wird nie negativ. Daher kann es nur (n + 1)-mal um 1 gesenkt werden. Andererseits können wir den Beweis auch im Rahmen der Buchhaltermethode interpretieren. Jede Durchführung von Schritt 3 wird auf die Initialisierung von j = 1 und eine Erhöhung von j in Schritt 5 angerechnet“, wobei jede Durchführung von Schritt 3 einen anderen ” Kostenträger erhält. Die Rechenzeitschranke folgt, da es nur n + 1 Kostenträger gibt, von denen jeder maximal eine Kosteneinheit erhält. Wir kommen nun zur effizienten Berechnung der Fehlerkanten. Es seien F (1), . . . , F (i − 1) bekannt. Dann ist für j = F (i−1) (p1 , . . . , pj−1 ) = (pi−j , . . . , pi−2 ) der längste echte Suffix von (p1 , . . . , pi−2 ), der auch Präfix ist. Für die Berechnung von F (i) wird (p1 , . . . , pi−1 ) untersucht. 30 Fall 1: pi−1 = pj . Dann ist (p1 , . . . , pj ) = (pi−j , . . . , pi−1 ) der längste passende Suffix. Also ist F (i) = F (i − 1) + 1. Fall 2: pi−1 6= pj . Die Situation stellt sich folgendermaßen dar: p1 . . . pj−j 0 +1 . . . pj−1 k pi−j k ... pi−j 0 k . . . pi−2 k Gesucht: p1 k . . . pj 0 −1 pj k − pi−1 ? pj 0 Für alle weiteren Kandidaten (p1 , . . . , pk ) muss (pj−k+1 , . . . , pj−1 ) = (p1 , . . . , pk−1 ) gelten. Der längste Kandidat ist (p1 , . . . , pj 0 ) mit j 0 = F (j). Nun muss die gleiche Fallunterscheidung wiederholt werden. Es muss getestet werden, ob pi−1 = pj 0 ist. Das Verfahren bricht ab, wenn das erste Mal Übereinstimmung festgestellt werden kann, also der 1. Fall erreicht wird, oder j 0 = 0 ist. In jedem Fall ist dann F (i) = j 0 + 1. Diese Überlegungen führen zu folgendem Algorithmus. Algorithmus 3.3.4: Input: m; P = (p1 , . . . , pm ) ∈ Σm . Output: F (1), . . . , F (m), die Fehlerkanten. 1.) Initialisierung: F (1) := 0; F (2) := 1; i := 3. 2.) Solange i ≤ m führe die Schritte 3, 4 und 5 aus. 3.) j := F (i − 1). 4.) Solange j 6= 0 und pj 6= pi−1 , setze j := F (j). 5.) F (i) := j + 1; i := i + 1. Satz 3.3.5: Algorithmus 3.3.4 berechnet die Fehlerkanten in O(m) Rechenschritten. Beweis: Die Korrektheit folgt aus unseren Vorüberlegungen. Für die Rechenzeit verwenden wir wieder die Buchhaltermethode. Es genügt zu zeigen, dass Schritt 4 nur insgesamt O(m) Rechenschritte kostet. Wir zählen die Erfolge (pj = pi−1 oder j = 0) und die Misserfolge (pj 6= pi−1 und j 6= 0), die in Schritt 4 auftreten. Die Zahl der Erfolge ist m − 2, da für jedes i ein Erfolg auftritt. Nach jedem Misserfolg wird j um mindestens 1 kleiner. Zu Beginn ist j = F (2) = 1, und stets gilt j ≥ 0. Wann wird j größer? Dies geschieht nur in Schritt 3, also höchstens (m − 2)-mal. Um wieviel wird j dabei größer? Der Parameter j erhält seinen letzten Wert in Schritt 4. Dann wird F (i) := j + 1, ineu := i + 1 und jneu := F (ineu − 1) = F (i) = j + 1. Also wird j jeweils nur um 1 erhöht. Also gibt es höchstens m − 1 Misserfolge. 2 Die Berechnung der Fehlerkanten baut eine Datenstruktur auf, die für das String Matching vieler Strings benutzt werden kann. Wir unterscheiden bei derartigen Problemen 31 die Preprocessing Zeit für den Aufbau einer Datenstruktur und die Query Zeit für die Verarbeitung einer Eingabe, hier eines Strings. Satz 3.3.6: Das String Matching Problem kann mit einer Preprocessing Zeit von O(m) und einer Query Zeit von O(n) gelöst werden. 32 4 Dynamische Programmierung 4.1 Ein Rückblick auf bekannte Beispiele Wir wissen um die Gefahr, bei rekursiven Algorithmen Teilprobleme wiederholt zu lösen. Das einfachste Beispiel ist die rekursive Formel für die Fibonacci-Zahlen: Fib(0) = 0, Fib(1) = 1, Fib(n) = Fib(n − 1) + Fib(n − 2). Eine naive rekursive Auswertung führt zu Fib(n) nicht trivialen Additionen (kein Summand ist 0) und damit zu exponentiell vielen Operationen. Der Ausweg ist naheliegend. Es werden Fib(2), . . . , Fib(n) iterativ berechnet. Dann genügen n − 1 Additionen und Platz für die Zahlen Fib(2), . . . , Fib(n). Der Speicherplatz kann sogar auf O(1) gesenkt werden, da wir nur die beiden zuletzt berechneten Werte abspeichern müssen. Die Methode der dynamischen Programmierung kann als Übertragung dieser Ideen auf Optimierungsprobleme angesehen werden. Sie ist anwendbar, wenn sich die Lösung eines Problems als optimale Verbindung von Lösungen für kleinere Teilprobleme ergibt. Dieser Zusammenhang zwischen dem Problem und den Teilproblemen wird durch die so genannte bellmansche Optimierungsgleichung ausgedrückt. Die Gültigkeit dieser Gleichung muss für jedes Problem nachgewiesen werden. Entscheidend ist, dass die Verbindung suboptimaler Lösungen nicht zu einer optimalen Lösung führen kann. Diese Eigenschaft ist bei Optimierungsproblemen keinesfalls selbstverständlich. So erhält man aus den weltbesten Spielern für die elf Positionen einer Fußballmannschaft nicht notwendigerweise die weltbeste Mannschaft. Wenn die Methode der dynamischen Programmierung anwendbar ist, führt sie zu einem effizienten Algorithmus, wenn – die Anzahl betrachteter Teilprobleme klein ist, – die Anzahl betrachteter Kombinationen von Teilproblemen klein ist und – die Berechnung des Wertes einer Kombination von Teilproblemen aus den Werten der Teilprobleme einfach ist. Dynamische Programmierung ist typischerweise speicherplatzintensiv, da die Lösungen für alle betrachteten Teilprobleme abgespeichert werden müssen. In jedem Fall benötigen wir eine partielle Ordnung auf der Menge der Teilprobleme, so dass für die Lösung eines Teilproblems nur auf Lösungen für kleinere Teilprobleme zurückgegriffen werden muss. Dies ermöglicht die Abarbeitung der Teilprobleme nach aufsteigender Größe. Beim Entwurf eines Algorithmus nach dem Prinzip der dynamischen Programmierung müssen wir uns überlegen, welches die geeigneten Teilprobleme sind und wie wir die Lösung eines Teilproblems auf die Lösung kleinerer Probleme zurückführen können. Dynamische Programmierung lässt sich besonders leicht für Probleme vom Intervalltyp“ ” einsetzen. Das Gesamtproblem betrifft Daten D1 , . . . , Dn und ist vom Typ (1, n). Als kanonische Teilprobleme ergeben sich die Probleme auf Di , . . . , Dj , also die Probleme vom 33 Typ (i, j), 1 ≤ i ≤ j ≤ n. Dies sind n2 + n ≤ n2 , also recht wenige Probleme. Wir können das Problem (i, j) disjunkt in Probleme (i, k) und (k + 1, j), i ≤ k ≤ j − 1, zerlegen, also wenige und kleinere Teilprobleme, wenn wir j − i als Größe des Problems (i, j) interpretieren. Wenn sich eine optimale Lösung für das Problem (i, j) bei optimaler Wahl von k und optimaler Zusammensetzung der Lösungen für (i, k) und (k + 1, j) ergibt (bellmansche Optimalitätsgleichung), dann können wir die Methode der dynamischen Programmierung anwenden. Es sei Z(n) die Zeit, um den Wert der aus den Lösungen für (i, k) und (k +1, j) zusammengesetzten Lösung für (i, j) zu berechnen, dann ist die Rechenzeit O(n3 Z(n)) (n2 Teilprobleme, je höchstens n Zerlegungspunkte, jeweils eine Wertberechnung). Hierfür kennen wir bereits zwei Anwendungen: – optimale Klammerung im Produkt von n Matrizen, wobei Z(n) = O(1) ist, und – den CYK-Algorithmus für das Wortproblem für kontextfreie Grammatiken in Chomsky-Normalform, wobei Z(n) = O(|G|) ist. Beim zweiten Beispiel haben wir es zwar mit einem Entscheidungs- und nicht mit einem Optimierungsproblem zu tun, aber die Vorgehensweise ist dieselbe. Beachtenswert ist die ∗ Erweiterung des Problems, so dass wir alle Variablen A mit A → wi . . . wj berechnen. Dies ist nötig, weil wir die Lösung der erweiterten Teilprobleme innerhalb der bellmanschen Optimalitätsgleichung brauchen. Ein anderes typisches Vorgehen ist es, Probleme dadurch einzuschränken, dass bestimmte Teile des Suchbereichs verboten“ werden. Hierfür kennen wir drei Beispiele: ” – kürzeste Wege zwischen allen Knotenpaaren in einem gerichteten bewerteten Graphen (APSP – all pairs shortest paths), – die Konstruktion regulärer Ausdrücke zu einer durch einen endlichen Automaten beschriebenen regulären Sprache, – das Rucksackproblem. Im ersten Beispiel sind im k-ten Teilproblem, 0 ≤ k ≤ n, nur die Knoten aus {1, . . . , k} als Zwischenknoten erlaubt. Ähnlich ist es im zweiten Beispiel (wieder kein Optimierungsproblem), in dem zuerst reguläre Ausdrücke für die Sprachen der Wörter produziert werden, bei denen bei Start in Zustand i am Ende der Zustand j erreicht wird, wenn als Zwischenzustände nur Zustände aus {1, . . . , k} erlaubt sind. Beim Rucksackproblem wird das Problem auf zweifache Weise eingeschränkt. Wir wiederholen das Vorgehen: Es gibt n Objekte, wobei das i-te Objekt das Gewicht gi und den Wert vi hat. Zusätzlich ist eine Gewichtsschranke G gegeben. Es soll nun eine Menge der Objekte bestimmt werden, die unter allen Mengen mit durch G beschränktem Gesamtgewicht den größten Wert hat. Beim Teilproblem (k, g), 1 ≤ k ≤ n, 1 ≤ g ≤ G, werden nur die ersten k Objekte betrachtet und die Gewichtsschranke wird auf g herabgesetzt. Wir sind an der Lösung des Problems (n, G) interessiert. Hier ist schon die Anzahl der Teilprobleme nicht mehr polynomiell, sondern nur noch pseudopolynomiell beschränkt. Da das 34 Rucksackproblem NP-hart ist, können wir aber auch nicht auf polynomielle Algorithmen hoffen, die das Problem exakt lösen. Es sei nun V (k, g) der Wert einer optimalem Lösung für das Problem (k, g). Dann sind folgende Randwerte sinnvoll: – V (k, g) = −∞ für g < 0, – V (0, g) = 0 für g ≥ 0, – V (k, 0) = 0. Da wir das k-te Objekt entweder einpacken oder nicht einpacken können, erhalten wir als bellmansche Optimalitätsgleichung für 1 ≤ k ≤ n und 1 ≤ g ≤ G: V (k, g) = max{V (k − 1, g), V (k − 1, g − gk ) + vk }. Jeder der nG Tabelleneinträge lässt sich aus den zuvor berechneten in Zeit O(1) berechnen. Also erhalten wir in Zeit O(nG) den Wert der optimalen Lösung und wenn wir uns stets merken, welcher der beiden Werte optimal ist, auch eine optimale Lösung. Der Algorithmus ist pseudopolynomiell und für polynomiell kleine Gewichtswerte polynomiell. 4.2 Ein weiterer pseudopolynomieller Algorithmus für das Rucksackproblem Der folgende Algorithmus für das Rucksackproblem ist im Gegensatz zu dem in Kapitel 4.1 vorgestellten Algorithmus polynomiell, wenn die Werte der Objekte polynomiell klein sind. Dieser Algorithmus wird in Kapitel 6 die Basis für Approximationsalgorithmen für das Rucksackproblem sein. Wieder schränken wir das Problem auf zweifache Weise ein. Allerdings ist die Denkweise eine andere. Wir wollen nun für k ∈ {1, . . . , n} und v ∈ {0, . . . , vsum } mit vsum := v1 + · · · + vn entscheiden, ob wir unter den Objekten 1, . . . , k eine Auswahl so treffen können, dass der Gesamtnutzen genau v beträgt, aber das Gesamtgewicht durch G beschränkt ist. Im positiven Fall wollen wir uns ein Paar (g, a) mit folgender Interpretation merken. Die Zahl g gibt das kleinste Gewicht an, mit dem wir das Teilproblem lösen können, und a gibt für eine unter diesen Lösungen an, ob wir das Objekt k einpacken (a = 1) oder nicht (a = 0). Mit dieser Zusatzinformation können wir später eine optimale Lösung effizient konstruieren. Das Teilproblem erhält die Bezeichnung (k, v). Das größte v, so dass der Eintrag für (n, v) nicht leer ist, ist offensichtlich der größte erreichbare Nutzenwert. Der zugehörige a-Wert gibt an, ob wir Objekt n einpacken sollen oder nicht. Im positiven Fall fahren wir mit dem Eintrag für (n − 1, v − vn ) fort, ansonsten mit dem Eintrag für (n − 1, v), um zu entscheiden, ob wir Objekt n − 1 einpacken sollen. Dieses Verfahren lässt sich iterieren. Wir müssen nun beschreiben, wie wir die Tabelleneinträge T (k, v) berechnen. Für k = 1 sind nur die Einträge (1, 0) (Eintrag (g, a) = (0, 0)) und (1, v1 ) (Eintrag (g, a) = (g1 , 1)) nicht leer, wobei wir für den zweiten Eintrag angenommen haben, dass g1 ≤ G ist. Sonst 35 wäre der Eintrag ebenfalls leer. Falls v < 0 ist, denken wir uns T (k, v) als leer. Zur Berechnung von T (k, v) haben wir für das Objekt k wiederum die beiden Möglichkeiten, es einzupacken oder nicht einzupacken. Im ersten Fall bleibt das Teilproblem (k −1, v −vk ) übrig, im zweiten Fall das Teilproblem (k − 1, v). Es gibt nun vier Fälle: – T (k − 1, v − vk ) und T (k − 1, v) sind leer, dann ist T (k, v) leer, – T (k − 1, v − vk ) = (g, a) und T (k − 1, v) ist leer, dann ist T (k, v) = (g + gk , 1), falls g + gk ≤ G, und sonst leer, – T (k − 1, v − vk ) ist leer und T (k − 1, v) = (g, a), dann ist T (k, v) = (g, 0), – T (k − 1, v − vk ) = (g, a) und T (k − 1, v) = (g 0 , a0 ), dann ist T (k, v) = (g + gk , 1), wenn g + gk ≤ g 0 ist, und T (k, v) = (g 0 , 0) sonst. Im ersten Fall gibt es für beide Möglichkeiten keine passende Lösung, im zweiten und dritten Fall nur in einem der beiden Fälle eine Lösung, die wir dann auch wählen. Nur im vierten Fall haben wir die Auswahl zwischen zwei Lösungen und wählen eine, die das Gewicht minimiert. In der Beschreibung des vierten Falls ist die bellmansche Optimalitätsgleichung versteckt: Wir füllen die Tabelle T zeilenweise. Dann genügt für jeden neuen Eintrag Zeit O(1). Damit haben wir folgenden Satz gezeigt. Satz 4.2.1: Das Rucksackproblem lässt sich in pseudopolynomieller Zeit O(nvsum ) = O(n2 vmax ) lösen, wobei vsum = v1 + · · · + vn und vmax = max{v1 , . . . , vn } ist. 4.3 Bioinformatik – die Ähnlichkeit zweier Sequenzen Spätestens seitdem das Human Genome Project sogar in den Nachrichten und den Tageszeitungen diskutiert worden ist, ist die Bedeutung der Genomforschung oder allgemeiner der Bioinformatik (besser der englische Ausdruck: computational molecular biology) allgemein anerkannt. Es gibt wohl kein Teilgebiet der Informatik, in dem die dynamische Programmierung eine größere Rolle spielt als in der Bioinformatik. Daher wollen wir hier, ohne den biologischen Hintergrund auszuleuchten, das Basisproblem untersuchen, die Ähnlichkeit zweier Sequenzen zu messen. Es seien die Sequenzen x = (x1 , . . . , xn ) und y = (y1 , . . . , ym ) gegeben, wobei xi und yj aus einem endlichen Alphabet Σ stammen. In der Biologie spielen zwei Alphabete eine besondere Rolle, das Alphabet {A, C, G, T } der vier Basen Adenin, Guanin, Cytosin und Thymin bei der DNA (bei der RNA ersetzt Uracil Thymin) und ein 20-elementiges Alphabet für die verschiedenen Aminosäuren. Wir nehmen an, dass eine Bewertungsfunktion (score function) s die Ähnlichkeit zweier Buchstaben misst, s : Σ×Σ → . Methoden zur Wahl praktisch guter Bewertungsfunktionen können nur in Spezialvorlesungen behandelt werden; daher werden wir im Folgenden die Existenz einer geeigneten Bewertungsfunktion voraussetzen. Es stellt sich die Frage, welches xi zu welchem yj gehört. Dazu nehmen wir an, dass die Sequenzen im Laufe der Zeit durch Mutationen, Einfügungen und Auslassungen verändert worden sind. Daher 36 wird das Alphabet um das Leerzeichen (oder den Lückenbuchstaben) −“ ergänzt (aber ” weiterhin Σ genannt). Nun suchen wir die Erweiterung x∗ = (x∗1 , . . . , x∗k ) von x (in x wurden Leerzeichen eingefügt) und die Erweiterung y ∗ = (y1∗ , . . . , yk∗ ) von y, so dass x∗ und y ∗ gleich lang sind und die Bewertung X s(x∗ , y ∗ ) := s(x∗i , yi∗ ) 1≤i≤k maximal ist. Wir nehmen an, dass s(−, −) < 0 ist. Daher können wir den Fall x∗i = yi∗ = − ausschließen. Damit wird das Problem endlich, da k ≤ n + m ist. Außerdem gehen wir davon aus, dass s(−, a) = s(b, −) = −d eine einheitliche Lückenstrafe ist (d > 0). Dieses Problem heißt global alignment (globale Anpassung) und kann mit dem Algorithmus von Needleman und Wunsch mit Hilfe der dynamischen Programmierung gelöst werden. Es sei S(i, j) die optimale Bewertung eines Alignments zwischen (x1 , . . . , xi ) und (y1 , . . . , yj ). Dann sind die Randwerte S(i, 0) = −id und S(0, j) = −jd vorgegeben und wir sind an S(n, m) interessiert. Wenn wir S(i, j) berechnen wollen, gibt es für die letzte Stelle in einem Alignment nur folgende Möglichkeiten: xi xi – . , , – yj yj Wir stoßen auf die Teilprobleme (i−1, j −1), (i−1, j) beziehungsweise (i, j −1). Aufgrund der Additivität der Bewertungsfunktion erhalten wir als bellmansche Optimalitätsgleichung S(i, j) = max{S(i − 1, j − 1) + s(xi , yj ), S(i − 1, j) − d, S(i, j − 1) − d}, die sich in Zeit O(1) auswerten lässt. Wir müssen jeweils nur die S-Werte von zwei Zeilen (oder zwei Spalten) abspeichern. Satz 4.3.1: Der Wert eines optimalen globalen Alignment kann in Zeit O(nm) auf Platz O(min{n, m}) berechnet werden. Wenn wir auch an einem optimalen Alignment interessiert sind, können wir uns an der Position (i, j) Zeiger auf die Positionen merken, für die das Maximum in der bellmanschen Optimalitätsgleichung angenommen wird. Wenn wir dann die gesamte Tabelle der Größe (n + 1)(m + 1) abspeichern, können wir von der Position (n, m) aus entlang der abgespeicherten Zeiger einen Weg zur Position (0, 0) wählen, wobei die Zeiger jeweils angeben, welche der drei Möglichkeiten für die letzte Position wir wählen. Wir haben also sogar eine kompakte Beschreibung aller global optimalen Alignments. Allerdings ist Platz (n + 1)(m + 1) kritisch, wenn n, m ≈ 106 sind. Hirschberg hat gezeigt, wie in Zeit O(nm) und Platz O(n + m) sogar ein global optimales Alignment berechnet werden kann. Dieser Ansatz ist auch deswegen so wichtig, weil sich die wesentliche Idee auch auf andere Algorithmen der dynamischen Programmierung anwenden lässt. Weiterhin bezeichne S(i, j) den Wert eines optimalen Alignments von 37 (x1 , . . . , xi ) und (y1 , . . . , yj ). Nun betrachten wir das Problem aber auch von der anderen Seite. Es sei S r (i, j) der Wert eines optimalen Alignments von (xn−i+1 , . . . , xn ) und (ym−j+1 , . . . , ym ). Natürlich lassen sich die S r -Werte analog zu den S-Werten berechnen. Wir schneiden nun ein optimales Alignment hinter xbn/2c auf. Dann gibt es ein k ∈ {0, . . . , m}, so dass y1 , . . . , yk zum linken Teil und yk+1 , . . . , ym zum rechten Teil des Schnitts gehören. Die Additivität der Bewertungsfunktion führt wieder dazu, dass wir ein global optimales Alignment aus optimalen Alignments des linken und des rechten Teils zusammenkleben“ können. Also gilt ” (∗) S(n, m) = max{S(bn/2c, k) + S r (dn/2e, m − k)|0 ≤ k ≤ m}. Nun können wir den Algorithmus von Hirschberg beschreiben. Algorithmus 4.3.2: 1.) In Zeit O(nm) und Platz O(m) werden die S(bn/2c, ·)-Werte berechnet und abgespeichert. 2.) In Zeit O(nm) und Platz O(m) werden die S r (dn/2e, ·)-Werte berechnet und abgespeichert. 3.) In Zeit O(m) wird das k ∗ , das (∗) maximiert, berechnet. Es wird (bn/2c, k ∗ ) abgespeichert, die Ergebnisse aus den ersten beiden Schritten können gelöscht werden. 4.) Löse rekursiv die Probleme für die Folgen (x1 , . . . , xbn/2c ) und (y1 , . . . , yk∗ ) sowie für die Folgen (xbn/2c+1, . . . , xn ) und (yk∗ +1 , . . . , ym ) und konkateniere die Lösungen. Die Rekursion endet in den trivialen Fällen, nämlich, wenn eine Folge leer wird oder eine Folge nur noch einen Buchstaben enthält (dann wird das Problem direkt gelöst). Satz 4.3.3: Der Algorithmus von Hirschberg löst das Problem des globalen Alignments konstruktiv in Zeit O(nm) auf Platz O(n + m). Beweis: Die Korrektheit ergibt sich aus den Vorüberlegungen. Der Platzbedarf ergibt sich, da bei jeder Problemzerlegung die Probleme kürzer“ werden, das heißt, die Ge” samtlänge der beiden beteiligten Sequenzen fällt, und stets nur O(1) Information gespeichert wird. Es bleibt die Abschätzung der Rechenzeit. Es sei die Konstante c so gewählt, dass die Schritte 1, 2 und 3 zusammen in Zeit cnm durchführbar sind und Probleme für n = 1 in Zeit cm und Probleme für m = 1 in Zeit cn lösbar sind. Außerdem betrachten wir o.B.d.A. nur den Fall, dass n eine Zweierpotenz ist (sonst Auffüllen mit einem neuen Buchstaben, der auf jeden Fall mit Lücken gepaart werden muss). Für die Zeitschranke T (n, m) gilt also T (n, m) ≤ cnm + max{T (n/2, k) + T (n/2, m − k)|0 ≤ k ≤ m}. Wir raten“ nun die Lösung T (n, m) ≤ 2cnm, was für n = 1 oder m = 1 korrekt ist. ” Induktiv folgt T (n, m) ≤ cnm + max{2c n2 k + 2c n2 (m − k)|0 ≤ k ≤ m} = cnm + max{cn(k + (m − k))|0 ≤ k ≤ m} = 2cnm. 2 38 Beim Problem des lokalen Alignment suchen wir Teilsequenzen (xi , . . . , xk ) und (yj , . . . , yl ), deren global optimales Alignment die höchste Bewertung hat. Wir wollen also aus den Sequenzen besonders ähnliche Teile herausschneiden. Der folgende Algorithmus stammt von Smith und Waterman. Es soll S ∗ (i, j) die beste Bewertung eines lokalen Alignments von (x1 , . . . , xi ) und (y1 , . . . , yj ) bezeichnen, wobei wir entweder xi verwenden oder keinen Buchstaben der x-Sequenz und entweder yj verwenden oder keinen Buchstaben der y-Sequenz. Da s(−, a) = s(b, −) = −d < 0 ist, ist S ∗ (0, 0) = S ∗ (i, 0) = S ∗ (0, j) = 0. Seien nun i, j ≥ 1. Dann haben wir folgende Möglichkeiten: – das Alignment zweier leerer Sequenzen mit Bewertung 0, – das Alignment einer leeren Sequenz mit einer nicht leeren Sequenz mit negativer Bewertung (daher muss dieser Fall nicht betrachtet werden) oder – Alignments, die xi und yj benutzen, wobei xi auf eine Lücke stoßen kann (Bewertung S ∗ (i − 1, j) − d) oder yj auf eine Lücke stoßen kann (Bewertung S ∗ (i, j − 1) − d) oder xi auf yj stößt (Bewertung S ∗ (i − 1, j − 1) + s(xi , yj )). Also gilt S ∗ (i, j) = max{0, S ∗ (i − 1, j) − d, S ∗ (i, j − 1) − d, S ∗ (i − 1, j − 1) + s(xi , yj )}. Schließlich ist ∗ Smax = max{S ∗ (i, j)|0 ≤ i ≤ n, 0 ≤ j ≤ m} die maximal erreichbare Bewertung, die mit den gleichen Ressourcen wie im Algorithmus von Needleman und Wunsch berechnet werden kann. Schließlich wollen wir affine Lückenkosten betrachten, dabei ist die Bewertung einer Lücke der Länge l in einer der beiden Sequenzen −c−dl, es gibt also eine Extrastrafe für die Eröffnung einer Lücke. Dies ist biologisch sinnvoll, da eher ganze Teilsequenzen verschwinden als an vielen Stellen einzelne Basen. Dies erschwert unsere Aufgabe, ein global optimales Alignment zu berechnen, da nun die Bewertung an einer Stelle von den vorherigen Stellen abhängt. Mit dieser Schwierigkeit können wir aber leicht fertig werden. Es sei A(i, j) die beste Bewertung eines Alignments von (x1 , . . . , xi ) und (y1 , . . . , yj ), wobei am Ende xi auf yj stößt, B(i, j) die beste Bewertung, wenn am Ende xi auf eine Lücke stößt, C(i, j) die beste Bewertung, wenn am Ende yj auf eine Lücke stößt und D(i, j) die beste Bewertung ohne Einschränkung. Mit diesem Trick, vier Parameter zu verwalten, kommen wir zu folgenden Gleichungen: A(i, 0) B(i, 0) C(i, 0) D(i, 0) A(i, j) B(i, j) C(i, j) D(i, j) = = = = = = = = A(0, j) = −∞, −c − di, B(0, j) = −∞, −∞, C(0, j) = −c − dj, B(i, 0), D(0, j) = C(0, j), D(i − 1, j − 1) + s(xi , yj ), max{B(i − 1, j) − d, D(i − 1, j) − c − d}, max{C(i, j − 1) − d, D(i, j − 1) − c − d}, max{A(i, j), B(i, j), C(i, j)}. 39 (In den Gleichungen für B und C können wir D durch A ersetzen.) In jedem Fall erhalten wir wieder einen O(nm)-Algorithmus. 4.4 Hidden Markoff Modelle Markoffketten mit verborgenen Zuständen (hidden Markoff models, HMMs) wurden erstmals im großen Umfang bei der automatischen Spracherkennung eingesetzt. Inzwischen finden sie zahlreiche Anwendungen, insbesondere auch in der Bioinformatik. Formal besteht ein HMM aus einer endlichen Zustandsmenge Q, auf der ein Markoffprozess abläuft, dessen Parameter akl (Übergangswahrscheinlichkeit von Zustand k in Zustand l, also Σl akl = 1) bekannt sind, dessen aktueller Verlauf aber nicht beobachtet werden kann, und einem endlichen Alphabet Σ, wobei zu jedem Zeitpunkt ein Buchstabe aus Σ nur in Abhängigkeit vom aktuellen Zustand erzeugt wird. Die Wahrscheinlichkeiten ek (b), im Zustand k den Buchstaben b zu erzeugen, sind bekannt. Die Folge der erzeugten Buchstaben kann beobachtet werden. Bei der automatischen Spracherkennung soll die Markoffkette die Spracherzeugung bei einem Menschen modellieren, während die erzeugten Buchstaben modellieren, was das Mikrofon aufgenommen hat. Hierbei sehen wir schon, dass wir in den Anwendungen das zusätzliche Problem haben, die Parameter akl und ek (b) zu schätzen. Hier wollen wir die Parameter als bekannt voraussetzen. Außerdem wird angenommen, dass zum Zeitpunkt 0 der Zustand 0 ist. Es seien nun π = (π1 , . . . , πn ) und x = (x1 , . . . , xn ). Dann beträgt die Wahrscheinlichkeit, dass das HMM die Zustandsfolge π durchläuft und dabei die Sequenz x beobachtet wird, Y P (x, π) = aπi−1 ,πi eπi (xi ), 1≤i≤n wobei π0 = 0 ist. Unsere Aufgabe ist es, aus den Beobachtungen auf die Zustandsfolge zu schließen. Das Decodierungsproblem besteht darin, zu x = (x1 , . . . , xn ) die Zustandsfolge π = (π1 , . . . , πn ) zu finden, die P (π|x) maximiert. Da P (π|x) = P (x, π)/P (x) ist, können wir auch P (x, π) maximieren. Der folgende Algorithmus heißt Viterbi-Algorithmus. Es sei vk (i) die größte Wahrscheinlichkeit eines Paares (x0 , π 0 ) mit x0 = (x1 , . . . , xi ), π 0 ∈ Qi und πi0 = k. Dann ist v0 (0) = 1, da die Markoffkette im Zustand 0 startet, und vk (0) = 0 für k 6= 0. Die bellmansche Optimalitätsgleichung lautet vk (i) = ek (xi ) · max{vm (i − 1) · amk |m ∈ Q}, da es einen Vorgängerzustand m geben muss und, falls πi−1 = m ist, mit Wahrscheinlichkeit amk der Zustand k erreicht und mit Wahrscheinlichkeit ek (xi ) der Buchstabe xi erzeugt wird. Wegen der Markoffeigenschaft genügt es, vom bisherigen Verlauf den letzten Zustand zu kennen. Außerdem hängt der erzeugte Buchstabe dann nur von dem neu erreichten Zustand ab. Es ist vmax = max{vk (n)|k ∈ Q} 40 die maximale P (x, π)-Wahrscheinlichkeit. Zur Berechnung genügt Zeit O(n|Q|2 ) und Platz O(n|Q|). Wenn wir uns jeweils merken, für welchen Zustand das Maximum angenommen wird, können wir auch die zugehörige Zustandsfolge berechnen. Ein weiteres Problem ist die Berechnung von P (πi = k|x) = P (x, πi = k)/P (x). Wir können dann den Zustand k(i) wählen, der diese Wahrscheinlichkeit maximiert. Wenn wir dies für jedes i tun, erhalten wir die Sequenz der (für jeden Zeitpunkt für sich genommen) wahrscheinlichsten Zustände. Wir berechnen P (x) mit dem so genannten ForwardAlgorithmus und P (x, πi = k) mit dem so genannten Backward-Algorithmus. Für den Forward-Algorithmus sei fk (i) = P (x1 , . . . , xi , πi = k). Dann ist P (x) die Summe aller fk (n), k ∈ Q. Da wir im Zustand 0 starten, ist f0 (0) = 1 und fk (0) = 0 für k 6= 0. Um für fk (i+1) eine Formel zu erhalten, die mit der dynamischen Programmierung ausgewertet werden kann, müssen wir eine kleine Rechnung ausführen: fk (i + 1) = P (x1 , . . . , xi+1 , πi+1 = k) (Definition) P = m P (x1 , . . . , xi , πi = m, xi+1 , πi+1 = k) (Summe über alle Möglichkeiten) P = m P (xi+1 , πi+1 = k|x1 , . . . , xi , πi = m)P (x1 , . . . , xi , πi = m) (Definition bedingte Wahrscheinlichkeit) P = m P (xi+1 , πi+1 = k|πi = m)fm (i) (Markoffeigenschaft, Definition fm (i)) P = m P (xi+1 |πi+1 = k, πi = m)P (πi+1 = k|πi = m)fm (i) (Definition bedingte Wahrscheinlichkeit) P = m P (xi+1 |πi+1 = k)P (πi+1 = k|πi = m)fm (i) (Markoffeigenschaft) P = ek (xi+1 ) m amk fm (i). Auch hier reichen Zeit O(n|Q|2 ) und Platz O(n|Q|). Wir kommen zur Berechnung von P (x, πi = k) mit dem Backward-Algorithmus, der ebenfalls mit Zeit O(n|Q|2 ) und Platz O(n|Q|) auskommen wird. Es ist P (x, πi = k) = P (x1 , . . . , xi , πi = k, xi+1 , . . . , xn ) = P (xi+1 , . . . , xn |x1 , . . . , xi , πi = k)P (x1 , . . . , xi , πi = k) = P (xi+1 , . . . , xn |πi = k) · P (x1 , . . . , xi , πi = k). Den zweiten Faktor kennen wir schon als fk (i) und den ersten Faktor nennen wir bk (i). Es ist bk (n) = 1, da wir die Beobachtung zum Zeitpunkt n beendet haben und daher nach dem Zeitpunkt n mit Sicherheit nichts“ beobachtet wird. Nun können wir bk (i) mit ” 41 ähnlichen Methoden wie zuvor fk (i) auswerten. Es ist bk (i) = = = = = = P (xi+1 , . . . , xn |πi = k) Σm P (πi+1 = m, xi+1 , . . . , xn |πi = k) Σm P (πi+1 = m|πi = k)P (xi+1 , . . . , xn |πi = k, πi+1 = m) Σm akm P (xi+1 , . . . , xn |πi+1 = m) Σm akm P (xi+2 , . . . , xn |πi+1 = m, xi+1 ) · P (xi+1 |πi+1 = m) Σm akm bm (i + 1)em (xi+1 ). Die von uns vorgestellten Algorithmen der dynamischen Programmierung ermöglichen also eine effiziente Analyse von HMMs. 42 5 5.1 Branch-and-Bound Algorithmen Die Vorgehensweise bei Branch-and-Bound Algorithmen Branch-and-Bound Algorithmen sind heuristische Optimierungsverfahren, mit denen mit Sicherheit eine optimale Lösung berechnet wird, bei denen aber für die Rechenzeit keine gute Garantie gegeben werden kann. Da Branch-and-Bound Algorithmen auf NP-harte Optimierungsprobleme angesetzt werden, kann auf eine polynomielle Schranke für die worst case Rechenzeit auch nicht gehofft werden. Jedoch besteht die Hoffnung, dass die Rechenzeit in vielen Fällen akzeptabel oder sogar klein ist. Ein weiterer Vorteil besteht darin, dass bei vorzeitigem Abbruch des Algorithmus (etwa aus Mangel an Rechenzeit) für die bis dahin beste berechnete Lösung eine Schranke für den Abstand des Wertes dieser Lösung vom optimalen Wert geliefert wird. Wir untersuchen hier nur Probleme, bei denen die Anzahl der Lösungen endlich ist und der Wert einer Lösung effizient berechnet werden kann. In unseren allgemeinen Betrachtungen gehen wir von Maximierungsproblemen aus. Eine Übertragung auf Minimierungsprobleme ist leicht möglich, wenn man sich klar macht, dass eine untere Schranke für den optimalen Wert eines Maximierungsproblems einer oberen Schranke für den optimalen Wert eines Minimierungsproblems entspricht“. ” Sei also nun ein Maximierungsproblem gegeben. Wir beschreiben die Module eines Branchand-Bound Algorithmus und ihr Zusammenspiel. Upper Bound Modul Es soll effizient eine (möglichst gute) obere Schranke U für den Wert einer optimalen Lösung berechnet werden. Oft können dabei Relaxationen des gegebenen Problems betrachtet werden. Bei Optimierungsproblemen gibt es meistens Nebenbedingungen, die das Problem erst schwierig machen. Wenn wir diese Bedingungen abschwächen (das Problem relaxter“ sehen), erhalten wir häufig ein effizient zu lösendes Optimierungsproblem. Da ” nun mehr Lösungen erlaubt sind, darunter alle Lösungen des gegebenen Problems, ist der Wert einer optimalen Lösung des relaxierten Problems (bei Maximierungsproblemen) eine obere Schranke für den Wert einer optimalen Lösung des eigentlichen Problems. Lower Bound Modul Es soll effizient eine (möglichst gute) untere Schranke L für den Wert einer optimalen Lösung berechnet werden. Greedy Algorithmen liefern zulässige Lösungen und der Wert jeder zulässigen Lösung ist (bei Maximierungsproblemen) eine untere Schranke für den Wert einer optimalen Lösung. Es hängt stark vom Problem ab, wie gut die durch greedy Algorithmen berechneten Lösungen sind. In jedem Fall ist der Wert der berechneten Lösung maximal U − L vom Wert einer optimalen Lösung entfernt. Wenn uns U − L klein genug erscheint, können wir die Suche abbrechen und haben eine genügend gute“ Lösung erhalten. Ist U = L, haben wir sogar ” eine optimale Lösung gefunden. Wir gehen nun davon aus, dass U − L > 0 ist und wir an einer garantiert optimalen Lösung interessiert sind. Natürlich kann die berechnete Lösung 43 näher als U −L am Optimum liegen und sogar optimal sein. Um eine optimale Lösung mit einer Garantie der Optimalität zu haben, müssen wir im Fall U − L > 0 noch arbeiten. Dazu wird das Problem zerlegt. Branching Modul Es werden Teilprobleme vom selben Typ erzeugt. Dabei ist ein Teilproblem P 0 von P ein Problem, bei dem die Menge zulässiger Lösungen eine echte Teilmenge der Menge zulässiger Lösungen von P ist und jede zulässige Lösung in P 0 denselben Wert wie in P hat. Günstig ist es, wenn die Teilprobleme (in einem vagen Sinn) gleich groß“ und ” gleich schwer“ sind, so dass die Teilprobleme kleiner“ und daher leichter“ sind. Eine ” ” ” Zerlegung in möglichst wenige Teilprobleme ist vorzuziehen. Alle unzerlegten Teilprobleme überdecken“ das Gesamtproblem in dem Sinn, dass die ” Vereinigung der Menge zulässiger Lösungen die Menge zulässiger Lösungen im Gesamtproblem ergibt. Wenn wir also alle unzerlegten Teilprobleme lösen und die beste dieser Lösungen auswählen, erhalten wir eine optimale Lösung des Gesamtproblems. Dazu berechnen wir für die unzerlegten Probleme obere Schranken Ui und untere Schranken Li . Da wir nun kleinere Probleme betrachten, sollte Ui ≤ U sein. Ansonsten können wir Ui durch U ersetzen. Es ist in jedem Fall U ∗ , das Maximum über alle Ui für unzerlegte Probleme, eine obere Schranke für das Gesamtproblem, da wir in keinem Teilproblem U ∗ übertreffen können. Damit wird U ∗ unsere neue obere Schranke U. Wir haben nun eine zulässige Lösung mit Wert L und für jedes Teilproblem eine Lösung mit Wert Li . Daher können wir L durch das Maximum all dieser Werte ersetzen. Allgemein gilt Lalt ≤ Lneu ≤ Uneu ≤ Ualt und die neue Maximaldifferenz zum Wert einer optimalen Lösung Uneu − Lneu ist nicht größer als Ualt − Lalt . So fahren wir fort. Das Verfahren ist endlich, da irgendwann für jedes unzerlegte Teilproblem die Anzahl zulässiger Lösungen 1 wird und wir bei den betrachteten Algorithmen den Wert dieser speziellen Lösung sowohl für die untere als auch für die obere Schranke des Teilproblems erhalten. Teilprobleme mit Ui = Li werden nicht zerlegt, da sie gelöst sind. Auf diese Weise erhalten wir eine Zerlegung des Problems, bis im schlechtesten Fall jedes unzerlegte Problem nur noch eine zulässige Lösung hat. In einem solchen Fall haben wir gegenüber einer erschöpfenden Suche (alle Lösungen ausprobieren) nichts gewonnen. Wieso hoffen wir, dass wir oft schnell sind? Nicht nur Teilprobleme mit Ui = Li müssen nicht zerlegt werden. Falls Ui ≤ L ist, kennen wir bereits eine zulässige Lösung, die von keiner zulässigen Lösung des i-ten Teilproblems übertroffen werden kann. Damit können wir das i-te Teilproblem als fertig bearbeitet einstufen. Das Ziel ist also, möglichst viele Teilprobleme möglichst früh als fertig bearbeitet einstufen zu können Dies gelingt einerseits durch Zerlegungen, die die oberen Schranken möglichst stark senken und andererseits durch das Auffinden möglichst guter zulässiger Lösungen. In Abhängigkeit davon, welches der beiden Ziele vielversprechender ist, unterscheiden wir Suchstrategien. Search Modul Es wird unter den unzerlegten Teilproblemen eines ausgewählt, das im nächsten Schritt 44 zerlegt werden soll (wobei dann für die neu entstehenden Teilprobleme obere und untere Schranken berechnet werden und danach die globalen Parameter U und L aktualisiert werden). Für die Wahl des zu zerlegenden Teilproblems gibt es grundsätzlich viele Strategien, die zwei wichtigsten sollen hier beschrieben werden. Wenn die unteren Schranken, also die Werte der berechneten zulässigen Lösungen, typischerweise gut sind, also recht nahe am Optimum liegen, dann ist es wichtig, die obere Schranke U zu senken. Vielleicht ist die beste berechnete zulässige Lösung bereits optimal und wir sind dabei, ihre Optimalität zu beweisen. In diesem Fall wählen wir ein unzerlegtes Problem mit Ui = U zur Zerlegung aus. Für dieses Problem gilt, dass dort optimistischerweise am meisten zu ho” len“ ist. Andererseits werden wir ein solches Problem auf jeden Fall zerlegen müssen, um eine garantiert optimale Lösung zu erhalten (greedy Suche). Wenn allerdings die unteren Schranken typischerweise schlecht sind, sind wir erst einmal an der Berechnung einigermaßen guter Lösungen interessiert, um überhaupt Teilprobleme als fertig bearbeitet ansehen zu können. Bei der Tiefensuche verfolgen wir zunächst einen Zweig, bis das zugehörige Teilproblem gelöst ist. Von den neu erzeugten Teilproblemen wählen wir jeweils eines, bei dem die obere Schranke am größten ist. 5.2 Ein Branch-and-Bound Algorithmus für das Rucksackproblem Beim Rucksackproblem besteht der Lösungsraum aus {0, 1}n , wobei x = (x1 , . . . , xn ) ∈ {0, 1}n beschreibt, dass genau die Objekte i mit xi = 1 eingepackt werden sollen. Lösungen sind zulässig, wenn g1 x1 + · · · + gn xn ≤ G ist. Für das Objekt i bezeichnen wir mit ei = vi /gi seine Effizienz, also die Nutzen-Kosten-Relation. Wir sortieren die Objekte vorab nach fallender Effizienz und nehmen (nach Umnummerierung der Objekte) an, dass e1 ≥ · · · ≥ en ist. Upper Bound Modul. Wir beschreiben das Rucksackproblem als Problem der binären Optimierung: v1 x1 + · · · + vn xn → max, wobei g 1 x1 + · · · + g n xn ≤ G und x1 , . . . , xn ∈ {0, 1}. Dieses Problem ist NP-hart. Wenn wir die letzte Bedingung durch 0 ≤ x 1 , . . . , xn ≤ 1 ersetzen, erhalten wir eine Relaxation des Problems. Die Größe des Lösungsraums ist zwar überabzählbar unendlich, aber es ist ein Problem der linearen Optimierung, das in polynomieller Zeit gelöst werden kann. Das Simplexverfahren hat zwar eine exponentielle worst case Rechenzeit, ist aber in der Praxis ungeschlagen. Unser spezielles Problem können wir mit einem greedy Algorithmus sogar in linearer Zeit lösen. Wir stellen uns vor, dass wir Objekt i in gi Teilobjekte mit Gewicht 1 und Nutzen ei = vi /gi zerlegen. Dann haben alle Teilobjekte das Gewicht 1. Natürlich ist es optimal, die G Teilobjekte mit maximalem Nutzen auszuwählen. Wenn wir ki Teile des i-ten Objektes gewählt haben, 45 entspricht das der Wahl von xi = ki /gi . Andererseits entspricht jede Wahl von xi ∈ [0, 1] der Wahl von einem Anteil von xi vom i-ten Objekt. Der Gewichtsanteil ist gi xi und der Nutzenanteil vi xi . Falls also ei > ej ist, ist es nicht optimal, einen Teil des j-ten Objekts zu wählen und einen Teil des i-ten Objektes nicht zu wählen. Dies beweist, dass der folgende Algorithmus das relaxierte Problem löst und somit eine obere Schranke U für das Rucksackproblem liefert. Algorithmus 5.2.1: 1.) Bestimme das größte i mit g1 + · · · + gi ≤ G und berechne G∗ = G − (g1 + · · · + gi ). 2.) Falls i = n, setze U = v1 + · · · + vn . 3.) Falls i < n, berechne xi+1 = G∗ /gi+1 und setze U = v1 + · · · + vi + vi+1 xi+1 . Lower Bound Modul. Hier benutzen wir eine einfache greedy Strategie. Wir betrachten die Objekte 1, . . . , n nacheinander. Wenn ein Objekt noch in den Rucksack passt, wird es eingepackt, und wenn es nicht mehr hineinpasst, wird es nicht eingepackt. Beachte, dass L ≥ v1 + · · · + vi für das in Algorithmus 5.2.1 berechnete i ist. Also ist U − L ≤ vi+1 . Search Modul. Da wir gerade gesehen haben, dass die untere Schranke ziemlich gut“ ist, ” wählen wir hier die in Kapitel 5.1 beschriebene greedy Suchstrategie. Branching Modul. Wir können ein Objekt j wählen und zwei Teilprobleme wie folgt bilden. In einem Teilproblem wird xj = 1 vorgeschrieben (Inklusion des Objekts j) und im anderen Teilproblem xj = 0 (Exklusion des Objekts j). Wir haben also nur zwei Teilprobleme gebildet und den Lösungsraum in zwei disjunkte gleich große Räume zerlegt. Die Exklusion können wir realisieren, indem wir das betroffene Objekt aus der Liste der Objekte entfernen. Auch die Inklusion wird so realisiert, allerdings wird G − gj als neue Gewichtsgrenze betrachtet (ist sie kleiner als 0, gibt es keine zulässige Lösung und das Teilproblem ist fertig bearbeitet). Zum Wert jeder berechneten Schranke und Lösung muss der Wert vj des eingepackten Objekts addiert werden. Wir haben noch die Freiheit zu wählen, welches Objekt j wir zur Problemzerlegung wählen. Sehr effiziente Objekte sind nicht geeignet, da bei ihnen bei Exklusion keine gute Lösung zu erwarten ist. Gleiches gilt bei der Inklusion eines sehr ineffizienten Objektes. Wir wählen das Objekt i + 1, das bei der Berechnung der oberen Schranke in Algorithmus 5.2.1 zerschnitten“ wurde. ” Es scheint eine kritische und damit wichtige Entscheidung zu sein, ob wir dieses Objekt einpacken oder nicht. Auf jeden Fall ist nun die Lösung des relaxierten Problems für beide Teilprobleme nicht mehr zulässig. 5.3 Branch-and-Bound Algorithmen für das TSP Das TSP ist ein Minimierungsproblem. Daher müssen die unteren Schranken mit Hilfe einer Relaxation berechnet werden. Die Kantenkosten seien c(i, j), wobei der Wert ∞ andeutet, dass die betreffende Kante nicht existiert. Wir beginnen mit einer Relaxation für das symmetrische TSP, bei dem c(i, j) = c(j, i) ist. Wenn wir aus einer TSP-Tour eine 46 Kante entfernen, erhalten wir einen Spannbaum. Somit bilden die Kosten eines minimalen Spannbaums eine untere Schranke für die Kosten einer optimalen Tour. Allerdings sind Touren keine Spannbäume. Das Problem der Berechnung eines minimalen Spannbaums ist also keine Relaxation des TSP. Mit einem kleinen Trick erhalten wir eine Relaxation. Definition 5.3.1: Ein 1-Baum ist ein Spannbaum auf den Knoten 2, . . . , n mit zwei zusätzlichen Kanten zum Knoten 1. Jede Tour enthält zwei Kanten, die den Knoten 1 berühren. Wenn wir sie entfernen, erhalten wir einen Spannbaum auf 2, . . . , n. Also bildet das Problem der Berechnung optimaler 1-Bäume eine Relaxation des TSP. Die Berechnung eines minimalen 1-Baums geschieht durch die Berechnung eines minimalen Spannbaums auf 2, . . . , n (Algorithmus von Kruskal) und der Wahl der beiden billigsten den Knoten 1 berührenden Kanten. Beim TSP liefern greedy Algorithmen keine guten Ergebnisse. Wir können die zugehörigen oberen Schranken zwar benutzen, aber es ist eine Tiefensuche als Suchstrategie anderen Strategien vorzuziehen. Es bleibt die Wahl einer geeigneten Branching-Regel. Die neuen Probleme entstehen jeweils durch Inklusion und/oder Exklusion von Kanten. Für das k-te Problem Pk beschreiben wir mit Ek die Menge der verbotenen Kanten und mit Ik die Menge der erzwungenen Kanten. Alle anderen Kanten bilden die Menge Fk = E − Ek − Ik der frei verfügbaren Kanten. Wir beginnen mit der einfachsten Regel. Branching-Regel 1 für die 1-Baum-Relaxation. Wähle im optimalen 1-Baum T einen Knoten i, der nicht Grad 2 hat und für den es eine freie Kante {i, j} in T gibt. Wähle unter den freien Kanten {i, l} in T eine mit minimalen Kosten. Durch Inklusion und Exklusion dieser Kante werden zwei neue Teilprobleme gebildet. Die Mengen zulässiger Lösungen für die beiden Teilprobleme sind disjunkt. Allerdings ist im durch Inklusion entstehenden Problem derselbe 1-Baum optimal wie im Problem, aus dem es entstand. An dieser Stelle soll ein neues Modul, Learning Modul genannt, hinzugefügt werden. Wir ziehen aus den erzwungenen Entscheidungen Konsequenzen. Wurden bereits zwei Kanten {i, j1 } und {i, j2 } erzwungen, können alle anderen Kanten an i verboten werden. Sind alle bis auf zwei Kanten an i verboten, können die verbleibenden Kanten als erzwungen deklariert werden. Werden auf diese Weise an einem Knoten mehr als zwei Kanten erzwungen, kann das Teilproblem als fertig bearbeitet (Wert ∞) betrachtet werden. Dies verbessert die berechneten Schranken und verhindert unnötige Problemzerlegungen. Die Tiefe des Branch-and-Bound Baums kann durch Lernprozeduren erheblich gesenkt werden. Wir nehmen an, dass in Zukunft derartige Lernprozeduren stets eingesetzt werden. Branching-Regel 2 für die 1-Baum-Relaxation. Die freien Kanten des optimalen 1Baumes werden nach wachsenden Kosten sortiert. Dann werden so lange Kanten gewählt, bis es einen Knoten p mit folgenden Eigenschaften gibt. Es gibt in Ik keine zwei Kanten an p, bei Inklusion aller gewählten Kanten gibt es zwei derartige Kanten. Es seien {i1 , j1 }, . . . , {iq , jq } die so gewählten Kanten. Dann werden q neue Probleme Pk,1 , . . . , Pk,q 47 gebildet mit Ik,r := Ik ∪ {{ih , jh }| 1 ≤ h ≤ r − 1} für 1 ≤ r ≤ q Ek,r := Ek ∪ {{ir , jr }} für 1 ≤ r ≤ q − 1 Ek,q := Ek ∪ {{i, j} ∈ / Ik,q | i = p oder j = p}. Diese Regel hat zwei Vorteile. Die entstehenden Teilprobleme sind disjunkt. Da jeweils eine Kante des optimalen 1-Baumes verboten wird, ist der momentan optimale 1-Baum in keinem Teilproblem mehr erlaubt. Wir können also nicht bei einem schlechten 1-Baum sitzen bleiben. Der Nachteil besteht in der oft zu großen Zahl q. Branching-Regel 3 für die 1-Baum-Relaxation. Wähle einen Knoten i, der im optimalen 1-Baum einen Grad größer als 2 hat und wähle zwei freie Kanten {i, j1 } und {i, j2 } in diesem 1-Baum. Es werden höchstens drei Teilprobleme gebildet. Ik,1 := Ik ∪ {{i, j1 }, {i, j2 }}, Ek,1 := Ek ∪ {{i, j}| j ∈ / {j1 , j2 }}, Ik,2 := Ik ∪ {{i, j1 }} , Ek,2 := Ek ∪ {{i, j2 }}, Ik,3 := Ik , Ek,3 := Ek ∪ {{i, j1 }}. Nach unseren Lernregeln hat jeder Knoten, der im optimalen 1-Baum einen Grad größer als 2 hat, mindestens zwei freie anliegende Kanten. Wenn dieser Knoten bereits eine anliegende erzwungene Kante hat, braucht das Problem Pk,1 nicht gebildet werden, da es sofort als fertig bearbeitet (Wert ∞) deklariert wird. Der zuvor optimale 1-Baum ist in keinem der Teilprobleme mehr erlaubt, da jeweils eine in diesem 1-Baum vorhandene Kante verboten ist. Die Anzahl der Teilprobleme ist klein und die Teilprobleme haben offensichtlich disjunkte Lösungsräume. Dennoch leidet unser Ansatz weiterhin daran, dass die berechneten unteren Schranken zu weit von den Kosten optimaler Touren entfernt sind. Dies ist anschaulich klar. Optimale 1-Bäume haben die Eigenschaft, dass zentrale“ Knoten hohen Grad und dezentrale“ ” ” Knoten Grad 1 haben. Wir zeigen nun, wie wir unsere Schranken veredeln“ können. ” Wir betrachten formal ein anderes Problem P 0 mit einer anderen Kostenfunktion c0 (i, j). Ohne weitere Einschränkung haben obere Schranken für P 0 keine Bedeutung für das eigentliche Problem P . Hier wählen wir aber c0 auf spezielle Weise. Wir setzen für jeden Knoten Strafkosten“ λi ∈ fest. Negative Strafen sind natürlich Belohnungen. Dann ist ” c0 (i, j) := c(i, j) + λi + λj . Da jede Tour π genau zwei den Knoten i berührende Kanten hat, folgt für λ = λ1 +· · ·+λn c0 (π) = c(π) + 2λ und wir erhalten aus einer unteren Schranke L0 für P 0 eine untere Schranke L := L0 − 2λ für P . Dies gilt, weil alle Touren dieselben Strafkosten erhalten. Unser Ziel besteht darin, die Strafkosten so zu wählen, dass 1-Bäume, die keine Touren sind, möglichst 48 stärker bestraft werden als Touren. Insbesondere sollte der für P berechnete optimale 1-Baum stark bestraft werden, da er ja die Berechnung einer besseren Schranke verhindert. Wir stellen heuristische Ideen zur Wahl geeigneter Strafkosten vor. Natürlich können wir mehrfach neue Strafen vergeben. Wenn die Verbesserung der unteren Schranke zu klein ausfällt oder gar keine Verbesserung erreicht wird, sollten wir uns wieder dem Branching widmen. Im Folgenden ist T der optimale 1-Baum vor der Veränderung der Kosten durch Strafkosten und T 0 der optimale 1-Baum nach Einführung der Strafkosten. Mit dT (i) und dT 0 (i) wird der Grad des Knotens i in den entsprechenden Bäumen bezeichnet. Strategie 1 für Strafkosten. Es wird λi := γ(dT (i) − 2) gewählt, also werden Knoten mit großem Grad bestraft und Knoten mit Grad 1 bekommen kleinere Kosten, also einen Anreiz, sie häufiger zu wählen. Die Wahl von γ ist kritisch. Zu kleine Werte haben keinen oder fast keinen Effekt, zu große Werte führen zu neuen Problemen. Für hoch bestrafte Knoten i gilt dT 0 (i) = 1, während für die Knoten mit dT (i) = 1 der Wert dT 0 (i) groß sein kann. Strategie 2 für Strafkosten. Für jeden Knoten i mit dT (i) 6= 2 wählen wir |λi | möglichst klein mit der Eigenschaft, dass dT 0 (i) 6= dT (i) ist, wenn λj = 0 für j 6= i gewählt wird. Hinterher werden aber alle λi -Werte simultan benutzt und der von uns gewünschte Effekt ist nicht mehr gesichert. Hierbei ist i 6= 1 und wir betrachten den Spannbaum T auf 2, . . . , n. Es sei dT (i) > 2. Dann soll λi ∈ möglichst klein gewählt werden, aber dT 0 (i) < dT (i) sichern, wenn wir nur den Knoten i bestrafen. Es sei {i, j} eine Kante in T . Wir entfernen {i, j} aus T . Damit zerfällt T in zwei Teilbäume. Es sei {l, m} mit l 6= i und m 6= i eine billigste Kante, die diese beiden Teilbäume verbindet. Die Kosten des neuen Baumes Tj sind um c∗ (j) := c(l, m) − c(i, j) ≥ 0 größer als die Kosten von T . Dann wird λi := min{c∗ (j) + 1|{i, j} ∈ T } gewählt. Alle Bäume T ∗ mit dT ∗ (i) ≥ dT (i) werden um mindestens dT (i)λi teurer. Es sei j ∗ so gewählt, dass λi = c∗ (j ∗ ) + 1. Dann ist Tj ∗ jetzt billiger als T und alle Bäume T ∗ mit dT ∗ (i) ≥ dT (i). möglichst klein gewählt werden, aber dT 0 (i) ≥ 2 Es sei dT (i) = 1. Dann soll −λi ∈ sichern, wenn wir nur Knoten i bestrafen“. Es sei {i, j} die i berührende Kante in T . ” Für jeden Knoten r 6∈ {i, j} fügen wir die Kante {i, r} zu T hinzu. Dadurch entsteht ein Kreis. Es sei {sr , tr } die teuerste Kante des Kreises, die i nicht berührt. Wenn wir {sr , tr } entfernen, erhalten wir einen Baum Tr∗ mit c(Tr∗ ) − c(T ) = c(i, r) − c(sr , tr ) ≥ 0. Es sei nun λi := max{c(sr , tr ) − c(i, r) − 1|r 6= i, (i, r) 6∈ T }. Alle Bäume T 0 mit dT 0 (i) 6= 1 werden um mindestens 2λi billiger, der Baum T dagegen nur um λi . Damit wird Tr∗ (wobei für r das Maximum in der Formel für λi angenommen wird) billiger als alle Bäume mit Knotengrad 1 für Knoten i. Trotz des erhöhten Rechenaufwandes hat sich diese Strategie in Anwendungen gut bewährt. Für asymmetrisches TSP (wobei das symmetrische TSP ein Spezialfall ist) können wir Zuordnungsprobleme, also spezielle Probleme der binären Optimierung, als Relaxation 49 wählen. Es seien xij ∈ {0, 1} Variablen, wobei xij = 1 beschreiben soll, dass die gerichtete Kante (i, j) gewählt wird. Wir fordern hier nur, dass wir für jeden Knoten eine eingehende und eine ausgehende Kante wählen. Die Lösung besteht also aus einer Überdeckung der Knoten durch disjunkte Kreise, nicht notwendigerweise einen Kreis wie bei einer Tour. Definition 5.3.2: Das Zuordnungsproblem besteht in folgender Aufgabe. X xij c(i, j) → min, wobei i6=j X xij = 1 für 1 ≤ j ≤ n, 1≤i≤n X xij = 1 für 1 ≤ i ≤ n, 1≤j≤n xij ∈ {0, 1}. Das Zuordnungsproblem kann mit der so genannten ungarischen Methode in O(n3 ) Schritten gelöst werden. Der Aufwand ist also größer als bei der Berechnung optimaler 1-Bäume. Allerdings sind die berechneten Schranken auch meistens besser. Auch hier lassen sich die Schranken veredeln“. Dies wollen wir nicht näher ausführen. ” Noch ein Hinweis zur ungarischen Methode. Wenn wir ein Zuordnungsproblem gelöst haben und ein Teilproblem wählen, bei dem eine Kante aus der Lösung verboten wird und ansonsten nur Kanten außerhalb der Lösung verboten und nur Kanten innerhalb der Lösung erzwungen werden, dann kann das neue Teilproblem in O(n2 ) Schritten gelöst werden. Dieser Hinweis ist also ein heißer Tipp, wie Branchingregeln aussehen sollten. Um die momentane Lösung des Zuordnungsproblems auszuschließen, sollten wir einen Kreis in der momentanen Lösung aufbrechen. Damit die Zahl der Teilprobleme klein bleibt, sollte dies ein möglichst kleiner Kreis sein. Branching-Regel 1 für die Zuordnungsproblem-Relaxation. Wähle einen kürzesten Kreis in der Lösung des Zuordnungsproblems. Wenn dieser Kreis q Kanten enthält, bilde q Teilprobleme, in denen jeweils eine Kante des Kreises verboten wird. Diese Regel hat den entscheidenden Nachteil, dass die Teilprobleme nicht disjunkt sind. Es kann also sein, dass wir uns mehrmals mit den gleichen Lösungen von Zuordnungsproblemen herumschlagen müssen, die weit von Touren entfernt sind. Dieser Nachteil wird durch folgende Regel behoben. Branching-Regel 2 für die Zuordnungsproblem-Relaxation. Ähnlich wie Branching-Regel 1. Im r-ten Teilproblem wird nicht nur die r-te Kreiskante verboten, zusätzlich werden die ersten r − 1 Kreiskanten durch Inklusion erzwungen. Um einen Kreis auf der Knotenmenge S aufzubrechen, muss es mindestens einen Knoten geben, dessen Nachfolger in der Lösung des Zuordnungsproblems nicht in S liegt. Branching-Regel 3 für die Zuordnungsproblem-Relaxation. Sei S die Knotenmenge eines kürzesten Kreises in der Lösung des Zuordnungsproblems. Falls q = |S| bilden 50 wir q Teilprobleme. Für jeden Knoten i in S bilden wir ein Teilproblem, bei dem alle Kanten (i, j) mit j ∈ S verboten sind. Auch hier sind die Teilprobleme nicht disjunkt. Die nächste Regel löst dieses Problem. Branching-Regel 4 für die Zuordnungsproblem-Relaxation. Sei S = {i1 , . . . , iq } die Knotenmenge eines kürzesten Kreises in der Lösung des Zuordnungsproblems. Im rten Teilproblem (1 ≤ r ≤ q) werden die folgenden Kanten verboten: (ir , j) für j ∈ S und (ih , j) für 1 ≤ h ≤ r − 1 und j ∈ / S, die früheren Kreisknoten müssen nun also Nachfolger in S haben. Es gibt keine befriedigende Erklärung dafür, dass sich in den Anwendungen die BranchingRegel 2 am besten bewährt hat. Zum Abschluss dieses Kapitels wollen wir anmerken, dass ein gutes Verständnis des betrachteten Problems und der Algorithmen zur Berechnung oberer und unterer Schranken notwendig ist, um gute Branch-and-Bound Algorithmen zu entwerfen. Hierfür sind auch theoretische Ergebnisse über das Verhalten der Algorithmen hilfreich. Einen Vergleich konkreter Branch-and-Bound Algorithmen, also Antworten auf Fragen wie z.B. mit welcher Branching-Strategie und welchen Algorithmen für obere und untere Schranken man die geringste Rechenzeit erzielt, wird man nur mit Experimenten erhalten. 51 6 6.1 Approximationsalgorithmen Approximationsalgorithmen für das metrische TSP Wenn es zu schwierig ist, Optimierungsprobleme exakt zu lösen, sollten wir uns mit Approximationslösungen zufrieden geben. Hier wollen wir Approximationsalgorithmen untersuchen, die eine bestimmte Güte der Lösung garantieren. Wenn wir beim TSP eine Tour mit Kosten 140 berechnen, während die optimalen Kosten 100 betragen, wie messen wir dann die Güte unserer Lösung? Es ist nicht vernünftig, die Differenz 140−100 = 40 zu wählen. Wenn wir nämlich alle Kostenwerte um den Faktor 10 erhöhen (eine andere Maßeinheit wählen), haben wir es praktisch mit demselben Problem zu tun. Die Güte derselben Lösung wäre aber auf 1400 − 1000 = 400 gestiegen. Daher wählen wir den Quotienten 140/100 = 1,4, der nach Änderung der Maßeinheit unverändert bleibt: 1400/1000 = 1,4. Definition 6.1.1: Bei Minimierungsproblemen messen wir die Güte einer Lösung durch den Quotienten aus dem Wert der berechneten Lösung und dem Wert einer optimalen Lösung. Bei Maximierungsproblemen messen wir die Güte durch den Quotienten aus dem Wert einer optimalen Lösung und dem Wert der berechneten Lösung. Durch diese Konvention ist die Güte eine Zahl, die mindestens 1 beträgt, wobei Güte 1 optimale Lösungen charakterisiert. Man muss sich daran gewöhnen, dass schlechtere Lösungen größere Gütewerte haben. Die Berechnung der Güte einer Lösung ist problematisch, da wir nicht optimale Lösungen ja nur betrachten, weil wir keine optimale Lösung kennen. Dennoch geht der Wert einer optimalen Lösung in die Güte der berechneten Lösung ein. Daher können wir die Güte oft nur abschätzen (wie übrigens auch die Rechenzeit). Definition 6.1.2: Ein Algorithmus A für ein Optimierungsproblem ist ein Approximationsalgorithmus mit Güte rA (n), wenn er stets Lösungen liefert, deren Güte durch rA (n) beschränkt ist. Wir betrachten hier zwei polynomielle Approximationsalgorithmen für das metrische TSP, dem Spezialfall des TSP, in dem die Kostenrelation symmetrisch (c(i, j) = c(j, i)) ist und die Dreiecksungleichung (c(i, j) ≤ c(i, k) + c(k, j)) erfüllt. Hierbei setzen wir folgende Ergebnisse über Eulerkreise in Multigraphen (Kanten dürfen mehrfach vorhanden sein) voraus. Ein Eulerkreis ist ein Kreis, auf dem Knoten wiederholt vorkommen dürfen, der aber jede Kante genau einmal enthalten muss. Ein Multigraph enthält genau dann einen Eulerkreis, wenn er bis auf isolierte Knoten zusammenhängend ist und jeder Knoten geraden Grad hat. In diesem Fall lässt sich ein Eulerkreis in linearer Zeit O(n + m) berechnen. Wenn wir einen Eulerkreis EK auf G = (V, E) haben, wobei Knoten doppelt benutzt werden dürfen, erhalten wir daraus in linearer Zeit eine Tour π, deren Kosten nicht größer als die Kosten von EK sind. Dazu durchlaufen wir EK. Jeden Knoten, den wir nicht zum ersten Mal erreichen (außer am Ende), lassen wir aus. Wegen der Dreiecksungleichung sind die direkten Wege, die wir nun gehen, nicht teurer als die Umwege auf EK. Der erste Approximationsalgorithmus benutzt die bekannte Tatsache (siehe Kapitel 5), dass ein optimaler Spannbaum nicht teurer als eine optimale Tour ist. Zudem sind Spannbäume 52 zusammenhängend und verbinden die gesamte Knotenmenge. Eine Verdoppelung aller Kanten ergibt für jeden Knoten einen geraden Grad. Der auf dieser Kantenmenge existierende Eulerkreis enthält jede Kante des Spannbaums zweimal. Die Kosten sind also durch die doppelten Kosten einer optimalen Tour nach oben beschränkt. Aus dem Eulerkreis erhalten wir eine Tour mit nicht größeren Kosten, also mit einer Güte, die nicht größer als 2 ist. Algorithmus 6.1.3: 1.) Berechne einen minimalen Spannbaum Sopt . 2.) Berechne den Multigraphen G, der jede Kante aus Sopt zweimal enthält. 3.) Berechne einen Eulerkreis EK auf G. 4.) Berechne aus EK eine Tour π mit C(π) ≤ C(EK). Satz 6.1.4: Algorithmus 6.1.3 hat Laufzeit O(n2 ) und ist ein Approximationsalgorithmus für das metrische TSP mit Güte 2. Beweis: Die Aussage über die Güte haben wir bereits oben gezeigt. Optimale Spannbäume lassen sich in Zeit O(n2 ) berechnen (Algorithmus von Prim). Da Spannbäume genau n − 1 Kanten haben, lassen sich die anderen Schritte in Zeit O(n) realisieren. 2 Der Nachteil dieses Verfahrens ist, dass wir den Grad von allen Knoten verdoppeln, um zu garantieren, dass er gerade wird. Der Algorithmus von Christofides vermeidet dies. Algorithmus 6.1.5: (von Christofides) 1.) Berechne einen minimalen Spannbaum Sopt . 2.) Berechne die Menge M der Knoten, die in Sopt einen ungeraden Grad haben. 3.) Berechne auf M ein Matching Mopt mit |M |/2 Kanten und minimalen Kosten. Dabei besteht ein Matching auf 2r Knoten aus r Kanten, die jeden Knoten berühren. 4.) Berechne den Multigraphen G, der aus einer Kopie von Sopt und einer Kopie von Mopt besteht. 5.) Berechne einen Eulerkreis EK auf G. 6.) Berechne aus EK eine Tour π mit C(π) ≤ C(EK). Satz 6.1.6: Der Algorithmus von Christofides hat Laufzeit O(n3 ) und ist ein Approximationsalgorithmus für das metrische TSP mit Güte 3/2. 53 Beweis: Schritt 1 kostet O(n2 ) Rechenschritte. Wenn wir alle Grade in einem Graphen aufsummieren, ist das Ergebnis immer gerade, da jede Kante zweimal gezählt wird. Daher ist |M | gerade und |M |/2 eine ganze Zahl. Die Berechnung von M ist in linearer Zeit möglich. Kostenminimale Matchings können in Zeit O(n3 ) berechnet werden, was wir hier nicht beweisen wollen. Da G höchstens 23 n Kanten enthält, lassen sich die Schritte 4, 5 und 6 in linearer Zeit durchführen. Die Aussage über die Güte wird folgendermaßen bewiesen. Es ist C(π) ≤ C(EK) = C(Sopt ) + C(Mopt ) ≤ Copt + C(Mopt ). Es genügt also zu zeigen, dass C(Mopt ) ≤ Copt /2 ist. Es sei i(1), . . . , i(2k) die Reihenfolge, in der die Knoten aus M auf einer optimalen Tour πopt vorkommen. Da wir ein metrisches TSP betrachten, sind die Kosten des Kreises (i(1), . . . , i(2k), i(1)) nicht größer als Copt , die Kosten von πopt . Der betrachtete Kreis enthält aber zwei disjunkte Matchings auf den Knoten in M , einerseits (i(1), i(2)), . . . , (i(2k − 1), i(2k)) und andererseits (i(2), i(3)), . . . , (i(2k − 2), i(2k − 1)), (i(2k), i(1)). Die Kosten des billigeren dieser beiden Matchings betragen also höchstens Copt /2. Dies gilt dann natürlich erst recht für Mopt . 2 Beispiele zeigen, dass wir weder in Satz 6.1.4 noch in Satz 6.1.6 die Güte durch eine kleinere Konstante abschätzen können. Wenn NP 6= P ist, gibt es für das allgemeine TSP keinen polynomiellen Approximationsalgorithmus, dessen Güte auch nur durch ein Polynom in n beschränkt ist. 6.2 Ein echt polynomielles Approximationsschema für das Rucksackproblem Approximationsalgorithmen werden um so besser, je näher die Güte an 1 herankommt. Es gibt NP-harte Optimierungsprobleme, bei denen es für jedes ε > 0 einen polynomiellen Approximationsalgorithmus mit Güte 1 + ε gibt. Wir sprechen von einem Approximationsschema, wenn es einen Algorithmus gibt, der neben der üblichen Eingabe eine Zahl ε > 0 als Eingabe erhält und dann in polynomieller Zeit eine Lösung mit Güte 1 + ε berechnet. Definition 6.2.1: Ein polynomielles Approximationschema (PTAS, polynomial time approximation scheme) ist für konstantes ε > 0 ein polynomieller Approximationsalgorithmus mit Güte 1 + ε. Es heißt echt polynomiell (FPTAS, fully PTAS), wenn die Rechenzeit bei variablem ε > 0 polynomiell in der Eingabelänge n und in ε−1 beschränkt ist. Polynomielle Approximationschemata erlauben die Berechnung von Lösungen mit guter Güte, aber die Laufzeit kann von der Größenordnung nd1/εe sein. So ein Algorithmus ist für jedes ε > 0 polynomiell, aber praktisch nicht für sehr kleine ε einsetzbar. Dies gilt für ein polynomielles Approximationschema für das euklidische TSP, bei dem die Orte Punkte in n sind und c(i, j) der euklidische Abstand der Punkte i und j ist. Bei echt polynomiellen Approximationschemata sind derartige Rechenzeiten ausgeschlossen. Wir 54 stellen hier ein echt polynomielles Approximationschema für das Rucksackproblem vor. Dazu erinnern wir uns an den pseudopolynomiellen Algorithmus für das Rucksackproblem aus Kapitel 4, der für kleine Nutzenwerte effizient ist. Was können wir tun, wenn die Nutzenwerte nicht klein sind? Wir streichen einfach die letzten t Stellen aller Nutzenwerte, die dadurch ungefähr um den Faktor 2−t kleiner werden. Dabei muss t groß genug sein, dass der pseudopolynomielle Algorithmus polynomiell wird, und klein genug, so dass die berechnete Lösung eine Güte von 1 + ε hat. Wir nehmen im Folgenden an, dass gi ≤ G für alle i ist. Objekte i mit gi > G können vorab gestrichen werden. Algorithmus 6.2.2: Es sei I = ((g1 , . . . , gn , v1 , . . . , vn , G), ε) die Eingabe für das echt polynomielle Approximationsschema. 1.) Berechne vmax := max{v1 , . . . , vn } und t := blog(εvmax /(n(1 + ε)))c. 2.) Berechne vi0 := bvi 2−t c für i = 1, . . . , n. 3.) Berechne eine optimale Lösung für die Eingabe I 0 = (g1 , . . . , gn , v10 , . . . , vn0 , G). Benutze dabei den Algorithmus aus Kapitel 4.2. Satz 6.2.3: Algorithmus 6.2.2 ist ein FPTAS für das Rucksackproblem mit Laufzeit O(n3 ε−1 ). Beweis: Zunächst einmal sind für I und I 0 dieselben Lösungen zulässig, da die Gewichtswerte nicht verändert wurden. Die ersten beiden Schritte von Algorithmus 6.2.2 kosten Zeit O(n). Schritt 3 kostet Zeit 0 O(n2 vmax ) = O(n2 vmax 2−t ) = O(n2 vmax n (1 + ε)/(εvmax )) = O(n3 ε−1 ). Schließlich müssen wir noch die Güte abschätzen. Es sei x ∈ {0, 1}n eine optimale Lösung für I und x0 die für I 0 berechnete optimale Lösung. Mit V (x) bezeichnen wir den Wert von x für I und mit V 0 (x) den Wert von x für I 0 . Unsere Behauptung lautet also V (x)/V (x0 ) ≤ 1 + ε. Wir beginnen mit einfachen Abschätzungen: P 0 0 V (x) ≥ V (x ) = (da x optimal für I ist) i v i xi P t 0 0 ≥ 2 P i v i xi (Definition von vi0 ) (da x0 optimal für I 0 ist) ≥ 2t Pi vi0 xi −t t = 2 Pi bvi 2 cxi −t t − 1)xi ≥ 2P i (vi 2 t ≥ v x − n2 i i i = V (x) − n2t . Somit ist 0 ≤ V (x) − V (x0 ) ≤ n2t ≤ εvmax /(1 + ε), 55 wobei wir im letzten Schritt die Definition von t benutzt haben. Da gi ≤ G ist, ist V (x) ≥ vmax . Wir können nämlich jedes Objekt alleine einpacken. Es folgt V (x) V (x0 ) + V (x) − V (x0 ) V (x) − V (x0 ) n2t = =1+ ≤1+ V (x0 ) V (x0 ) V (x0 ) V (x) − n2t ε 1 εvmax /(1 + ε) =1+ · ≤ 1+ vmax − εvmax /(1 + ε) 1 + ε 1 − ε/(1 + ε) 1+ε ε · = 1 + ε. = 1+ 1+ε 1+ε−ε 2 Haben wir für das Rucksackproblem Glück gehabt oder gibt es allgemeine Methoden, um FPTAS zu entwerfen? 6.3 Methoden zum Entwurf polynomieller Approximationsschemata Wir haben in Kapitel 6.2 ein FPTAS für das Rucksackproblem entworfen. Dabei sind wir von einem pseudopolynomiellen Algorithmus ausgegangen und haben ad hoc die Methode des Rundens“ auf die Nutzenwerte angewendet. Hier wollen wir die drei wichtigsten Me” thoden, um PTAS und manchmal sogar FPTAS zu entwerfen, vorstellen. In den folgenden drei Unterkapiteln werden wir diese Methoden exemplarisch auf Lastverteilungsprobleme (Scheduling Probleme) anwenden. Alle drei Methoden gehen von den drei Komponenten Suchraum, Lösungsraum und Algorithmen zur exakten Lösung des Optimierungsproblems aus, wobei die exakten Algorithmen natürlich exponentielle Zeit benötigen. Die Methoden unterscheiden sich darin, welche Komponente manipuliert wird. In Kapitel 6.2 haben wir die Eingaben verändert, wir haben den komplexen Raum möglicher Eingaben so geglättet“, dass die neuen Ein” gaben effizient zu behandeln waren und exakte Lösungen für das geglättete Problem gut für das Ursprungsproblem sind. Diese Methode wird in Kapitel 6.4 näher vorgestellt. Die zweite Möglichkeit (Kapitel 6.5) besteht darin, den Lösungsraum zu glätten. Dabei wird der Lösungsraum so aufgeteilt, dass es effizient möglich ist, in jedem Teilbereich eine relativ gute Lösung zu finden, so dass die beste dieser Lösungen bezogen auf den ursprünglichen Lösungsraum fast optimal ist. Schließlich (Kapitel 6.6) können wir den Verlauf des gegebenen Algorithmus so vereinfachen, dass er effizient wird und gute Lösungen liefert. 6.4 PTAS durch Glättung des Suchraums Die Algorithmen, die mit der Methode Glättung des Suchraums“ arbeiten, gehen nach ” folgendem Schema vor: 56 – In Abhängigkeit von der angepeilten Güte, also in Abhängigkeit von ε > 0, wird aus der gegebenen Eingabe I eine geglättete oder vereinfachte Eingabe I 0 berechnet. – Es wird eine optimale Lösung x0 für I 0 berechnet. – Aus x0 wird eine Lösung x für I berechnet, deren Güte durch 1 + ε beschränkt ist. Natürlich ist bei diesem Ansatz die Auswahl eines geeigneten I 0 kritisch. Wenn I 0 der ursprünglichen Eingabe I zu ähnlich ist, wird es nicht effizient möglich sein, eine optimale Lösung für I 0 zu berechnen. Wenn I 0 jedoch zu verschieden von I ist, haben wir mit x0 kaum Information für die Lösung von I gewonnen. Bevor wir diesen Ansatz an einem konkreten Beispiel ausprobieren, wollen wir vier erfolgreich eingesetzte Methoden zur Glättung von Suchräumen nennen: 1. Runden von Zahlen. Wie in Kapitel 6.2 werden Zahlen, dort die Nutzenwerte, auf das nächstkleinere Vielfache einer Zweierpotenz 2m gerundet. (Anschließend können die m Nullen am Ende der Zahlen oft gestrichen werden.) 2. Verschmelzen von Stücken. Wenn wir viele Aufgaben auf verschiedene Maschinen zu verteilen haben, können wir einige zu einer großen Aufgabe zusammenfassen. Dies verringert die Anzahl der Aufgaben, aber auch die Freiheiten bei der Verteilung der Aufgaben auf die Maschinen. 3. Wegschneiden. Es können besonders irreguläre Teile der Eingabe ignoriert werden. 4. Anpassen. Es können ähnliche Objekte durch gleiche Objekte mit mittleren Parametern ersetzt werden. Aus k Aufgaben mit ähnlichen Bearbeitungszeiten können wir Aufgaben mit derselben Bearbeitungsdauer machen. Diese kann der Mittelwert oder der Median der betrachteten Bearbeitungszeiten sein. Wir betrachten hier das Beispiel, n Aufgaben, deren Bearbeitungszeiten t1 , . . . , tn betragen, so auf zwei Maschinen zu verteilen, dass die maximale Laufzeit der beiden Maschinen minimiert wird. Das Teilproblem, ob wir beide Maschinen gleich stark belasten können, ist das als NP-vollständig bekannte Partitionsproblem. Wir leiten zunächst eine einfache untere Schranke ab. Es sei tsum := t1 + · · · + tn und tmax := max{t1 , . . . , tn }. Dann ist L := max{tsum /2, tmax } eine untere Schranke für OPT, den Wert einer optimalen Lösung. Natürlich muss die Aufgabe mit Bearbeitungszeit tmax auf einer Maschine durchgeführt werden und genauso offensichtlich ist, dass eine der Maschinen mindestens die halbe Last erhält. Wenn wir alle Aufgaben der ersten Maschine zuteilen, ist ihre Belastung tsum ≤ 2L. Also ist es trivial, eine Lösung mit Güte 2 anzugeben. Daher sei im Folgenden ε ∈ (0, 1). Eine entscheidende Idee ist nun die Aufteilung der Aufgaben in große Aufgaben (ti > εL) und kleine Aufgaben (ti ≤ εL). Nach dieser Einteilung lässt sich die geglättete Eingabe I 0 wie folgt beschreiben. Sie übernimmt alle großen Aufgaben von I. Wenn S die Gesamtbearbeitungsdauer aller kleinen Aufgaben ist, gibt es in I 0 genau bS/(εL)c Aufgaben, die jeweils die Bearbeitungsdauer εL haben. Wir 57 verschmelzen die kleinen Aufgaben also zunächst und zerhacken die entstehende Aufgabe in Stücke der Größe εL, wobei wir ein mögliches Reststück, dessen Länge kleiner als εL ist, ignorieren. Wir haben die Methode des Verschmelzens als Zwischenschritt benutzt, um eigentlich die Methode des Anpassens anzuwenden. Am Rande wurde die Methode des Wegschneidens benutzt, indem wir das irreguläre Reststück ignoriert haben. Hinter diesem Ansatz steckt die Idee, dass Aufgaben mit kurzer Bearbeitungsdauer nicht die ganz großen Probleme verursachen können. Die Zeit für diesen Schritt lässt sich durch O(n + k ∗ ) beschreiben, wobei k ∗ die Anzahl der neuen Aufgaben ist. Diese müssen ja aufgezählt werden, um I 0 in der üblichen Weise zu beschreiben. Bevor wir dazu kommen, zu beschreiben, wie wir I 0 optimal lösen, wollen wir OPT0 in Relation zu OPT setzen. Dazu betrachten wir eine optimale Verteilung der Aufgaben in I. Es sei Si die Gesamtlast, die die i-te Maschine auf diese Weise durch kleine Aufgaben erhält. Wir konstruieren eine Lösung für I 0 , indem wir die großen Aufgaben bei ihren Maschinen belassen und die kleinen Aufgaben entfernen. Anschließend erhält Maschine i maximal dSi /(εL)e der neuen kleinen Aufgaben. Da dS1 /(εL)e + dS2 /(εL)e ≥ (S1 + S2 )/(εL) ≥ bS/(εL)c ist, können wir auf diese Weise alle neuen kleinen Aufgaben verteilen. Die Belastung der i-ten Maschine erhöht sich maximal um dSi /(εL)eεL − Si ≤ (Si /(εL) + 1)εL − Si = εL. Also ist, da OPT ≥ L, OPT0 ≤ OPT + εL ≤ (1 + ε)OPT. Nun kommen wir zur Lösung von I 0 . Dazu schätzen wir zunächst ab, wieviele Aufgaben I 0 zu verteilen hat. Die Gesamtbearbeitungszeit t0sum der Aufgaben von I 0 ist nach Konstruktion nicht größer als tsum und damit maximal 2L. Da alle Aufgaben in I 0 eine Bearbeitungszeit von mindestens εL haben, kann es maximal 2ε−1 Aufgaben geben. Diese Anzahl ist von n unabhängig! Wir werden in diesem Unterkapitel nur ein PTAS entwerfen. Dann können wir alle Aufgabenverteilungen zwischen den Maschinen ausprobieren −1 (maximal 22ε viele) und die beste auswählen. Für konstantes ε ist die Rechenzeit O(1). Diese Konstante ist groß, aber etwa für ε = 1/10 gut handhabbar. Natürlich können wir alle besseren Algorithmen als den Probieralgorithmus benutzen. Die Rechenzeit bis hier−1 her ist also O(n) + O(ε−1 22ε ), wobei wir für den zweiten Summanden die Zeit für die Bewertung jedes Lösungsversuchs mit O(ε−1 ) abgeschätzt haben. Bei einer geschickten Reihenfolge der Lösungsversuche kann dieser Faktor eingespart werden. Abschließend müssen wir die optimale Lösung x0 für I 0 in eine Lösung x für I übersetzen. Es sei L0i die Last der i-ten Maschine bezüglich x0 . Diese Last teilt sich in die Last Bi0 für die großen Aufgaben und Si0 für die neuen kleinen Aufgaben. Also ist L0i = Bi0 + Si0 . Darüber hinaus ist S10 + S20 = εLbS/(εL)c > S − εL. 58 Wir konstruieren die Lösung x für I in Zeit O(n) wie folgt. Wir belassen die großen Aufgaben da, wohin sie gemäß x0 zugeordnet worden sind. Für die kleinen Aufgaben reservieren wir auf Maschine 1 ein Zeitintervall der Länge S10 + εL und auf Maschine 2 ein Zeitintervall der Länge S20 + εL. Dabei wird ein greedy Algorithmus verwendet, der erst Maschine 1 bedient. Da jede kleine Aufgabe eine Bearbeitungsdauer von höchstens εL hat, erhält Maschine 1 eine Last von mindestens S10 . Die Restlast bezüglich der kleinen Aufgaben ist also maximal S − S10 und dies ist nach den obigen Berechnungen höchstens S20 + εL. Damit gilt für die Last Li gemäß dieser Aufgabenverteilung Li ≤ Bi0 + (Si0 + εL) = L0i + εL ≤ (1 + ε)OPT + εOPT = (1 + 2ε)OPT. Hierbei haben wir die Ungleichungen L ≤ OPT und L0i ≤ OPT0 ≤ (1 + ε)OPT benutzt. Insgesamt erhalten wir ein PTAS für das betrachtete Lastverteilungsproblem. 6.5 PTAS durch Glättung des Lösungsraums Die Algorithmen, die mit der Methode Glättung des Lösungsraums“ arbeiten, gehen ” nach folgendem Schema vor: – In Abhängigkeit von der angepeilten Güte wird der Lösungsraum in (möglichst disjunkte) Bereiche aufgeteilt. – Für jeden Bereich wird eine Lösung berechnet, deren Wert nahe am Wert einer für den Bereich optimalen Lösung liegt. Hierbei können Bereiche, von denen man weiß, dass sie keine optimale Lösung enthalten können, unbeachtet bleiben. – Es wird die beste der Approximationslösungen als Gesamtlösung präsentiert. Natürlich ist darauf zu achten, dass die Anzahl der zu betrachtenden Bereiche nicht zu groß wird. Wenn jedoch die Bereiche nicht wesentlich kleiner und einfacher“ als der ” gesamte Lösungsraum werden, können wir nicht darauf hoffen, dass es einfacher wird, einzelne Bereiche zu betrachten als gleich den ganzen Lösungsraum. Wir wenden diesen Ansatz nun auf das schon in Kapitel 6.4 untersuchte Lastverteilungsproblem an. Dabei benutzen wir dieselbe Aufteilung in große und kleine Aufgaben. Daher wissen wir aus Kapitel 6.4, dass es maximal 2ε−1 viele große Aufgaben gibt. Es gibt also −1 höchstens 22ε Möglichkeiten, die großen Aufgaben auf die beiden Maschinen zu verteilen. Wir bilden für jede Aufteilung der großen Aufgaben einen Bereich. Die Anzahl der −1 Bereiche ist also durch 22ε und damit für konstantes ε durch eine Konstante beschränkt. Im Folgenden betrachten wir einen Bereich, also eine festgelegte Aufteilung der großen Aufgaben. Die Verteilung der kleinen Aufgaben ist nicht kritisch, die falsche“ Zuweisung ” einer kleinen Aufgabe ergibt auch nur einen kleinen Fehler“. Daher verwenden wir hier ” einen greedy Algorithmus, bei dem die jeweils nächste Aufgabe der Maschine zugeteilt wird, die bisher weniger ausgelastet ist. Sind beide Maschinen gerade gleich belastet, wählen wir eine beliebige der beiden Maschinen. Auf diese Weise genügt für jeden Bereich 59 eine Rechenzeit von O(n). Die anschließende Wahl einer besten aus den konstruierten Lösungen erhöht die Gesamtrechenzeit nicht wesentlich. Es bleibt zu zeigen, dass für jeden Bereich Rm die Maximallast Lm der berechneten Lösung durch (1 + ε)OPTm beschränkt ist, wobei OPTm die Maximallast einer besten Lösung aus Rm ist. Es sei Bi die bereits durch die großen Aufgaben der Maschine i zugewiesene Last. Nach Definition von Rm ist B := max{B1 , B2 } ≤ OPTm . Falls Lm = B ist, folgt Lm = OPTm und wir haben für Rm eine optimale Lösung berechnet. Falls Lm > B ist, sei o.B.d.A. Lm die Auslastung der Maschine 1. Da Lm > B1 ist, wurde in der greedy Phase noch eine Aufgabe an M1 gegeben. Wir betrachten den Zeitpunkt, zu dem M1 die letzte Aufgabe, also eine kleine Aufgabe, zugewiesen bekommt. Da zu diesem Zeitpunkt M1 nicht stärker als M2 ausgelastet ist, ist die Auslastung von M1 zu diesem Zeitpunkt durch tsum /2 beschränkt. Also folgt Lm ≤ tsum /2 + εL ≤ L + εL = (1 + ε)L ≤ (1 + ε)OPT ≤ (1 + ε)OPTm . Damit haben wir ein weiteres PTAS für das betrachtete Lastverteilungsproblem entworfen. 6.6 FPTAS durch Glättung der Zwischenlösungen eines langsamen Algorithmus Die Algorithmen, die effizient gute Approximationslösungen berechnen, indem sie von einem langsamen, aber exakten Optimierungsalgorithmus ausgehen und die Mengen der ” Zwischenlösungen“ glätten, benötigen einen exakten Optimierungsalgorithmus, der sehr strukturiert vorgeht. Wir erläutern diesen Ansatz wieder an dem schon in Kapitel 6.4 und 6.5 untersuchten Lastverteilungsproblem. Eine Eingabe I besteht aus n Teilen, nämlich den n Aufgaben (Jobs) J1 , . . . , Jn mit den Bearbeitungsdauern t1 , . . . , tn . Wir erhalten auf natürliche Weise so genannte Präfixprobleme Ik , 1 ≤ k ≤ n, wobei Ik das Lastverteilungsproblem für die Aufgaben J1 , . . . , Jk ist. Insbesondere ist es unser Ziel, In = I zu lösen. Jede zulässige Lösung von Ik kann durch einen Vektor aus d charakterisiert werden. Für d = 2 beschreiben wir mit (L1 , L2 ) die Lasten für die beiden Maschinen. Es ist in konstanter Zeit möglich, den Wert der Lösung, nämlich max{L1 , L2 }, aus der Charakterisierung (L1 , L2 ) zu berechnen. Ein langsamer, exakter Algorithmus kann so vorgehen, dass er beim Übergang von Ik−1 zu Ik nur die Charakterisierungen der zulässigen Lösungen abspeichert. Für jedes Paar (x, y) mit 0 ≤ x, y ≤ tsum speichern wir, ob es eine zulässige Lösung mit Charakteristik (x, y) gibt. Jeder nun auch zulässig genannte Vektor (x, y) kann in konstanter Zeit bearbeitet werden, das heißt, es können alle aus ihm entstehenden zulässigen Vektoren für Ik berechnet werden, dies sind offensichtlich (x + tk , y) und (x, y + tk ). Am Ende kennen wir alle zulässigen Vektoren für I = In , berechnen für sie den Wert und bilden das Minimum dieser Werte. Die Rechenzeit beträgt O(nt2sum ), also haben wir einen pseudopolynomiellen Optimierungsalgorithmus. An dieser Stelle wollen wir bemerken, dass wir den Algorithmus so beschrieben haben, dass die allgemeine Idee deutlich wird. Wir können die Rechenzeit auf O(ntsum ) drücken, da für Ik für alle zulässigen Vektoren (x, y) die Gleichung x + y = t1 + · · · + tk gilt und wir uns daher nur x merken müssen. Dies ist aber für die Entwicklung unseres FPTAS nicht relevant. 60 Wie soll nun die Glättung der Menge der Zwischenlösungen aussehen? Wir betrachten die zulässigen Vektoren als Punkte des Quadrates [0, tsum ] × [0, tsum ] ⊆ 2 . Wir zerlegen dieses Quadrat in (L + 1)2 Rechtecke, indem wir auf beiden Koordinaten [0, tsum ] in die Intervalle [0, 1), [1, ∆), [∆, ∆2 ), [∆2 , ∆3 ), . . . , [∆L−1 , tsum ] zerlegen. Dazu wählen wir ∆ := 1 + ε 2n Da ∆L ≥ tsum sein soll, wählen wir L := dlog∆ tsum e = d(ln tsum )/ ln ∆e. Aus der Taylorreihe für ln z folgt ln z ≥ 1 − 1z , also ist ln ∆ ≥ 1 − und 1/ ln ∆ ≤ Damit ist L≤ ε/(2n) 1 ε = 1 + 2n 1 + ε/(2n) 2n 1 + ε/(2n) =1+ . ε/(2n) ε 2n 1+ ε ln tsum . Die wesentliche Idee besteht nun darin, sich für jedes Rechteck nur einen zulässigen Vektor zu merken, da sich alle Vektoren eines Rechtecks relativ ähnlich“ sind. Dies können wir ” quantifizieren. Falls [x1 , y1 ] und [x2 , y2 ] in dasselbe Rechteck fallen, gilt x1 /∆ ≤ x2 ≤ x1 ∆ und y1 /∆ ≤ y2 ≤ y1 ∆. Da ∆ sehr nahe bei 1 liegt, ist der Begriff relativ ähnlich“ gerechtfertigt. Für jedes Recht” eck speichern wir nur einen zulässigen Vektor und nur aus den für Ik−1 gespeicherten Vektoren erzeugen wir zulässige Vektoren für Ik . Damit haben wir es für jedes Präfixproblem Ik mit maximal (L + 1)2 zulässigen Vektoren zu tun, die jeweils Rechenzeit O(1) verursachen. Die Gesamtrechenzeit beträgt also O(nL2 ) = O(n3 ε−2 ln2 tsum ) und ist somit polynomiell in der Eingabelänge, die mindestens n und mindestens ln(tsum ) beträgt, und ε−1 . Es bleibt zu zeigen, dass die Güte der berechneten Lösungen durch 1 + ε beschränkt ist. Dazu beweisen wir, dass es zu jedem für Ik zulässigen Vektor (x, y) einen vom Approximationsalgorithmus für Ik berechneten zulässigen Vektor (x0 , y 0 ) mit x0 ≤ ∆k x und y 0 ≤ ∆k y gibt. Zunächst leiten wir aus dieser Behauptung das Ergebnis über die Güte her. Es sei (x, y) eine optimale Lösung. Die Behauptung impliziert, das der Approximationsalgorithmus eine Lösung (x0 , y 0 ) liefert mit Wert max{x0 , y 0 } ≤ max{x∆n , y∆n }, denn 61 der Approximationsalgorithmus präsentiert die beste der berechneten Lösungen. Es ist also max{x0 , y 0 } ≤ ∆n max{x, y} = ∆n OPT. Es bleibt zu zeigen, dass ∆n ≤ 1 + ε ist. Es ist für ε ≤ 1 n ε 3 ε ε 2 ε/2 n /2! + /3! + · · · ≤ 1 + ε. ∆ = 1+ ≤ eε/2 = 1 + + n 2 2 2 Wir können uns auf den Fall ε ≤ 1 beschränken, da wir für ε > 1 alle Aufgaben an Maschine 1 geben können. Abschließend beweisen wir die übrig gebliebene Behauptung. Es sei (x, y) zulässig für Ik . Dann wird behauptet, dass der Approximationsalgorithmus einen für Ik zulässigen Vektor (x0 , y 0 ) mit x0 ≤ ∆k x und y 0 ≤ ∆k y berechnet. Wir beweisen die Behauptung durch Induktion über k. Für k = 1 werden (0, t1 ) und (t1 , 0) erzeugt. Da t1 ≥ 1 ist, liegen sie in verschiedenen Rechtecken und werden beide übernommen. Für den Induktionsschritt sei (x, y) zulässig für Ik . Dann entstand (x, y) aus (x − tk , y) oder aus (x, y − tk ), wir nehmen hier o.B.d.A. an, dass (x, y) aus (x − tk , y) entstand. Dann ist (x − tk , y) zulässig für Ik−1 und nach Induktionsvoraussetzung berechnet der Approximationsalgorithmus eine Lösung (x0 , y 0 ) mit x0 ≤ ∆k−1 (x − tk ) und y 0 ≤ ∆k−1 y. Im nächsten Schritt erzeugt der Approximationsalgorithmus (x0 + tk , y 0 ). Diese Lösung wird eventuell zugunsten einer zulässigen Lösung (x00 , y 00 ) vergessen. Dann gilt aber x00 ≤ ∆(x0 + tk ) und y 00 ≤ ∆y 0 und somit auch x00 ≤ ∆(∆k−1 (x − tk ) + tk ) ≤ ∆k x und y 00 ≤ ∆k y. Also haben wir ein FPTAS für das Lastverteilungsproblem entworfen. Entscheidend ist aber, dass wir die drei wesentlichen Ansätze zum Entwurf von PTAS und manchmal auch FPTAS an einem Beispiel kennengelernt und erprobt haben. 62 7 Lineare Optimierung, Randomisiertes Runden und Derandomisierung 7.1 Lineare Optimierungsprobleme Lineare Optimierung bezeichnet die Optimierung linearer Funktionen unter linearen Nebenbedingungen. Die Standardform dieser Problemklasse lautet minimiere f (x1 , . . . , xn ) = w1 x1 + · · · + wn xn unter den Nebenbedingungen und ai1 x1 + · · · + ain xn ≥ bi für 1 ≤ i ≤ m xj ≥ 0 für 1 ≤ j ≤ n. In der Literatur werden lineare Optimierungsprobleme meistens mit dem etwas missverständlichen Begriff Lineare Programme und das Lösen linearer Optimierungsprobleme mit dem Begriff Lineare Programmierung bezeichnet. Erstaunlich viele Probleme, besonders aus dem Bereich BWL, lassen sich als lineare Optimierungsprobleme formulieren. Lineare Optimierungsprobleme lassen sich auch geometrisch veranschaulichen: Den Lösungen entsprechen Belegungen der Variablen x1 , . . . , xn , also Punkte aus n . Die Nebenbedingungen der Form xj ≥ 0 erzwingen, dass nur Punkte mit positiven Koordinaten zulässig sind. Jede Nebenbedingung der Form ai1 x1 + · · · + ain xn ≥ bi beschreibt eine Hyperebene im n und erzwingt, dass nur Punkte auf einer Seite dieser Hyperebene zulässig sind. Man sieht leicht ein, dass Punktmengen, die durch Hyperebenen begrenzt sind, konvexe n-dimensionale Polyeder sind. Aus der Form der Zielfunktion f (x) = w1 x1 +· · ·+wn xn kann man schließen, dass Punkte mit gleichem Zielfunktionswert ebenfalls auf einer Hyperebene liegen. Das Ziel besteht also darin, die Hyperebene der Zielfunktion auf dem Polyeder der zulässigen Punkte so weit wie möglich parallel zu verschieben, so dass der Wert der Zielfunktion minimal wird. Wem diese geometrische Veranschaulichung zu abstrakt ist, sollte Beispiele mit n = 2 oder n = 3 ausprobieren. Man sieht leicht, dass einige Varianten von linearen Optimierungsproblemen auf die Standardform zurückgeführt werden können. Wenn die Zielfunktion f (x) maximiert werden soll, können wir stattdessen −f (x) minimieren. Mit demselben Argument können wir ≤-Ungleichungen durch ≥-Ungleichungen simulieren, indem wir beide Seiten mit −1 multiplizieren. Schließlich können lineare Nebenbedingungen der Form ai1 x1 + · · · + ain xn = bi durch eine ≥- und eine ≤-Ungleichung simuliert werden. Für die Lösung von linearen Optimierungsproblemen sind die folgenden Ansätze bekannt, die wir hier nur erwähnen, aber nicht im Detail behandeln wollen: – Der Simplexalgorithmus von Dantzig (1963). Diese Algorithmus führt auf den Ecken des Polyeders der zulässigen Punkte eine lokale Suche durch, bis er einen lokal optimalen Punkt gefunden hat. Es kann dann gezeigt werden, dass dieser auch global optimal ist. Der Simplexalgorithmus gilt als praktisch effizient, allerdings kann man (künstliche) Beispiele konstruieren, auf denen er exponentielle Rechenzeit erfordert. 63 – Die Ellipsoidmethode von Khachian (1979). Dieses war der erste Polynomialzeitalgorithmus für lineare Optimierung. Das Polyeder der zulässigen Punkte wird durch ein Ellipsoid angenähert, was sukzessive verkleinert werden kann. Die Ellipsoidmethode gilt als praktisch nicht effizient. – Innere-Punkt-Methoden (z.B. Karmarkar (1984)). Diese Methoden kombinieren die Zielfunktion mit einer Strafkostenfunktion, die verhindert, dass man das Polyeder der zulässigen Punkte verlässt. Es werden dabei Funktionen gewählt, die dann mit Hilfe von nichtlinearen Optimierungsverfahren aus der Analysis (Newton-Methode) minimiert werden können. Einige Varianten dieser Verfahren haben polynomielle worst-case Rechenzeiten und gelten auch als praktisch effizient. Eine wichtige Variante von linearen Optimierungsproblemen sind so genannte ganzzahlige Optimierungsprobleme. Hier fügt man die zusätzliche Nebenbedingung ein, dass die Variablen nur ganzzahlige Werte annehmen dürfen. Dies ist eine nahe liegende Bedingung, wenn die Variablen z.B. Stückzahlen beschreiben. In der Literatur spricht man auch von ganzzahliger Programmierung oder Integer Programming. Wir sehen sofort, dass der Lösungsraum kleiner geworden ist. Von dem Polyeder der zulässigen Punkte sind nur noch die Punkte mit ganzzahligen Koordinaten zulässig. Der mögliche erste Eindruck, dass das Problem durch die Verkleinerung des Lösungsraums einfacher wird, täuscht allerdings. Zum Beispiel führt die lokale Suche auf den Ecken des Polyeders nicht mehr zum Ziel und man überlegt sich leicht Beispiele, wo der Eckpunkt des Polyeders mit optimalem Wert der Zielfunktion und der Punkt mit ganzzahligen Koordinaten und optimalem Wert der Zielfunktion sehr weit auseinander liegen. Das Lösen ganzzahliger Optimierungsprobleme ist NP-hart. Dies haben wir sogar schon in dieser Vorlesung gezeigt, da wir in Kapitel 5 gesehen haben, dass sich das Rucksackproblem als ganzzahliges lineares Optimierungsproblem (mit sogar nur einer linearen Nebenbedingung) beschreiben lässt. Es ist eine gute Übungsaufgabe, ähnliche Beschreibungen für andere NP-harte Optimierungsprobleme zu finden. In diesem Kapitel wollen wir die folgende nahe liegende Idee für die Konstruktion von Algorithmen für NP-harte Probleme behandeln: Formuliere das Problem als ganzzahliges lineares Optimierungsproblem; relaxiere die Ganzzahligkeitsbedingungen; löse das entstandene lineare Optimierungsproblem in polynomieller Zeit; führe eine Nachbehand” lung“ der möglicherweise nicht ganzzahligen Lösung durch, um eine ganzzahlige Lösung zu erhalten. Es sollte klar sein, dass im Allgemeinen entweder der letzte Schritt nicht effizient durchführbar ist oder dass er die Qualität der Lösung verschlechtert, da wir anderenfalls P = NP bewiesen hätten. Wir wollen diesen Ansatz in diesem Kapitel für die Optimierungsprobleme MAXSAT und MAXkSAT vorführen. Wir definieren zunächst diese Probleme und geben die Modellierungen als ganzzahlige Optimierungsprobleme an. Im folgenden Abschnitt behandeln wir zunächst einen einfachen randomisierten Algorithmus für MAXSAT und MAXkSAT. Dann stellen wir eine Technik für die o.g. Nachbehandlung“ vor, das randomisierte Run” den, und analysieren den entstandenen randomisierten Algorithmus. Anschließend stellen 64 wir eine allgemeine Technik vor, um Randomisierung aus Algorithmen wieder zu entfernen. Dieser Vorgang wird als Derandomisierung bezeichnet. Insgesamt erhalten wir deterministische Approximationsalgorithmen für MAXSAT und MAXkSAT. MAXSAT ist folgendermaßen definiert: Eingabe ist eine Menge X = {x1 , . . . , xn } von Variablen und eine Menge C = {C1 , . . . , Cm } von Klauseln über diesen Variablen. Eine Klausel ist dabei eine Disjunktion von Variablen und negierten Variablen aus X. Die Aufgabe besteht darin, eine Belegung für die Variablen in X zu finden, so dass die Anzahl der dadurch erfüllten Klauseln maximal ist. Im Gegensatz zum Erfüllbarkeitsproblem SAT brauchen also nicht alle Klauseln erfüllt zu werden. MAXSAT ist allerdings NPhart, da die gegebene Klauselmenge genau dann (im Sinne von SAT) erfüllbar ist, wenn die maximale Anzahl der erfüllten Klauseln gleich der Anzahl der vorhandenen Klauseln ist. Beim Problem MAXkSAT haben wir die Einschränkung an die Eingabe, dass jede Klausel genau k verschiedene Literale enthält. Wir formulieren MAXSAT als ganzzahliges lineares Optimierungsproblem. Maximiere m X zj j=1 unter den Nebenbedingungen yi , zj ∈ {0, 1} für alle i = 1, . . . , n und j = 1, . . . , m und X X yi + i | xi kommt in Klausel Cj vor (1 − yi ) ≥ zj i | xi kommt in Klausel Cj vor für j = 1, . . . , m. Die Belegung von yi entspricht dem Wert der Variablen xi aus der MAXSAT-Eingabe. Die Variable zj soll angeben, ob die Klausel Cj erfüllt ist oder nicht. Es wird also die Anzahl erfüllter Klauseln maximiert. Der Ausdruck X X yi + (1 − yi ) i | xi kommt in Klausel Cj vor i | xi kommt in Klausel Cj vor gibt an, wie viele Literale in der Klausel Cj erfüllt sind. Die zugehörige Nebenbedingung erzwingt dann, dass nur dann zj den Wert 1 annehmen kann, wenn mindestens ein Literal in der Klausel Cj erfüllt ist. Lösungen für das MAXSAT-Problem entsprechen daher Lösungen für das angegebene ganzzahlige Optimierungsproblem und umgekehrt. Wir beachten hierbei, dass der Begriff Lösung“ für eine Variablenbelegung steht, die die Ziel” funktion maximiert, und nicht für eine Variablenbelegung, die nur alle Nebenbedingungen erfüllt. Da MAXkSAT gegenüber MAXSAT nur durch die Klausellänge eingeschränkt ist, ist das angegebene ganzzahlige Optimierungsproblem auch eine Modellierung von MAXkSAT. Die einzige Besonderheit besteht darin, dass die linearen Nebenbedingungen jeweils nur noch k + 1 Variablen enthalten. 65 7.2 Ein einfacher randomisierter Algorithmus für MAXSAT und MAXkSAT Sei eine Eingabe ({x1 , . . . , xn }, {C1 , . . . , Cm }) für MAXkSAT gegeben. Der randomisierte Algorithmus A würfelt eine Belegung der Variablen unabhängig und gemäß Gleichverteilung aus, d.h., jede Variable wird mit Wahrscheinlichkeit 1/2 mit 0 oder 1 belegt. Offensichtlich ist die Rechenzeit des Algorithmus A linear. Bereits dieser Algorithmus erfüllt im Durchschnitt viele Klauseln, sofern k nicht zu klein ist. Satz 7.2.1: Die erwartete Anzahl von erfüllten Klauseln ist m · (1 − 1/2k ). Beweis: Sei Xi die Zufallsvariable, die nur die Werte 0 und 1 annimmt und die angibt, ob die i-te Klausel Ci erfüllt ist. Dann ist E[Xi ] = Prob(i-te Klausel nicht erfüllt) · 0 + Prob(i-te Klausel erfüllt) · 1 = Prob(i-te Klausel erfüllt). Sei die i-te Klausel (nach Umbenennung der Variablen) x1 ∨ · · · ∨ xk . Diese Klausel ist nur dann nicht erfüllt, wenn alle Variablen x1 , . . . , xk den Wert 0 annehmen, was mit Wahrscheinlichkeit 1/2k passiert. Also E[Xi ] = 1 − 1/2k . Sei X die Zufallsvariable, die die Anzahl erfüllter Klauseln angibt. Dann ist X = X1 + · · · + Xm . Wegen der Linearität des Erwartungswertes folgt E[X] = m · (1 − 1/2k ). 2 Man überprüft leicht, dass die Analyse auch für die (geringfügig erweiterte) MAXkSATVariante gilt, bei der jede Klausel mindestens k Literale enthält. Leider kann beim Problem MAXSAT der Fall k = 1 nicht ausgeschlossen werden, so dass wir für MAXSAT nur beweisen können, dass im Erwartungswert mindestens die Hälfte der Klauseln erfüllt wird. Offensichtlich können höchstens alle Klauseln erfüllt werden. Man bezeichnet daher den Algorithmus A auch als randomisierten Algorithmus mit einer erwarteten worst case Güte von 2. Im folgenden Abschnitt wollen wir diesen Algorithmus verbessern. 7.3 Randomisiertes Runden Wir relaxieren die Ganzzahligkeitsbedingungen in dem ganzzahligen Optimierungsproblem aus Abschnitt 7.1, d.h., wir ersetzen die Bedingungen xi , yj ∈ {0, 1} durch 0 ≤ xi ≤ 1 bzw. 0 ≤ yj ≤ 1. Dann können wir mit einem der oben beschriebenen Verfahren das entstandene lineare Optimierungsproblem lösen. Wie aber sollen wir nun z.B. den Wert 0,63 für eine Variable yi interpretieren? Wir benutzen einfach den folgenden Trick: Wenn in der Lösung des linearen Optimierungsproblems die Variable yi den Wert pi ∈ [0, 1] hat, erhält xi den Wert 1 mit Wahrscheinlichkeit pi und den Wert 0 mit Wahrscheinlichkeit 1 − pi . Dabei werden verschiedene Variablen unabhängig voneinander belegt. Den gesamten Algorithmus nennen wir im Folgenden Algorithmus B. Der Algorithmus A aus dem letzten Abschnitt war also der Spezialfall von Algorithmus B, wobei die Wahrscheinlichkeiten nicht durch Lösung eines linearen Optimierungsproblems bestimmt wurden, sondern einfach als 1/2 gewählt wurden. Es ist an dieser Stelle aber überhaupt nicht klar, warum randomisiertes Runden zu einem guten Algorithmus führen soll; daher wollen wir den Algorithmus B nun genauer analysieren. 66 Das lineare Optimierungsproblem ohne die Ganzzahligkeitsbedingungen ist eine Relaxation des eigentlich betrachteten Optimierungsproblems. Daher ist der maximal mögliche Zielfunktionswert der Relaxation mindestens genauso groß wie der maximal mögliche Zielfunktionswert des ganzzahligen Optimierungsproblems. Der Wert der Lösung des relaxierten Problems ist also eine obere Schranke für den Wert der Lösung des ursprünglichen Problems. Wir wollen nun analysieren, mit welcher Wahrscheinlichkeit die einzelnen Klauseln erfüllt werden. Im Folgenden bezeichnen wir die (berechnete) Lösung des relaxierten Optimierungsproblems mit ybi bzw. zbj . Lemma 7.3.1: Sei die j-te Klausel eine Klausel der Länge k. Die Wahrscheinlichkeit, dass sie durch randomisiertes Runden erfüllt wird, ist mindestens " k # 1 · zbj . 1− 1− k Beweis: Wir benutzen im Beweis die beiden folgenden Aussagen. Die erste entspricht der Aussage, dass der geometrische Mittelwert nicht größer als der arithmetische Mittelwert ist, die zweite kann mit einfacher Analysis bewiesen werden. Aussage 1: Seien a1 , . . . , ak ≥ 0. Dann gilt a1 · · · ak ≤ ((a1 + · · · + ak )/k)k . Aussage 2: Für x ∈ [0, 1] gilt 1 − (1 − xk )k ≥ (1 − (1 − k1 )k ) · x. Betrachte (ggf. nach Umbenennung der Variablen) die Klausel x1 ∨· · ·∨xk . Beim randomiQ sierten Runden wird die Klausel mit Wahrscheinlichkeit ki=1 (1− ybi ) nicht erfüllt, also mit Q Wahrscheinlichkeit 1 − ki=1 (1 − ybi ) erfüllt. Da yb1 , . . . , ybn , zb1 , . . . , zc m eine Lösung des relaxierten Optimierungsproblems ist, folgt yb1 +· · ·+ybk ≥ zbj und damit (1−yb1 )+· · ·+(1−ybk ) ≤ k − zbj . Mit Aussage 1 folgt k Y i=1 (1 − ybi ) ≤ k − zbj k k , k k Y zbj also 1 − (1 − ybi ) ≥ 1 − 1 − . k i=1 Wegen Aussage 2 ist dieser Ausdruck mindestens (1 − (1 − k1 )k ) · zbj . Wir bemerken, dass k 1 1 ≥ 1 − ≈ 0.632 ist. 2 für alle k gilt, dass 1 − 1 − k e Satz 7.3.2: Sei opt die Anzahl an Klauseln, die man maximal gleichzeitig erfüllen kann. Die erwartete Anzahl von Klauseln, die in der Lösung von Algorithmus B erfüllt sind, beträgt mindestens (1 − 1e ) · opt. Beweis: Wie oben bemerkt, liefert der Wert der Lösung des relaxierten ProblemsPeine obere Schranke für den Wert der Lösung des ursprünglichen Problems, d.h., opt ≤ zbj . Die erwartete Anzahl erfüllter Klauseln beträgt dann mindestens 67 X j " k # X 1 1− 1− · zbj ≥ (1 − 1/e) · zbj ≥ (1 − 1/e) · opt. k j 2 Wir zeigen nun, dass eine Kombination der Algorithmen A und B zu einer noch besseren erwarteten Güte führt. Der Algorithmus C führt für eine MAXSAT-Eingabe nacheinander die Algorithmen A und B aus und wählt von den beiden berechneten Variablenbelegungen diejenige aus, die die meisten Klauseln erfüllt. Satz 7.3.3: Sei eine Menge von Klauseln gegeben und sei opt die Anzahl an Klauseln, die maximal gleichzeitig erfüllt werden kann. Algorithmus C liefert eine Variablenbelegung, so dass die erwartete Anzahl erfüllter Klauseln mindestens 0.75 · opt beträgt. Beweis: In unseren obigen Analysen haben wir als Nebenprodukt erhalten, dass bei randomisiertem Runden mit Parametern 1/2, . . . , 1/2 (Algorithmus A) eine Klausel der Länge k mit Wahrscheinlichkeit 1−2−k erfüllt ist und bei randomisiertem Runden mit Parametern yb1 , . . . , ybn (Algorithmus B) mit einer Wahrscheinlichkeit von mindestens 1 − (1 − k1 )k · zbj erfüllt ist. Wir vergleichen diese Wahrscheinlichkeiten: k 1 2 3 4 5 1 − 2−k 0.5 0.75 0.875 0.938 0.969 1 − (1 − k1 )k 1.0 0.75 0.704 0.684 0.672 Man kann ahnen, dass Algorithmus A auf langen Klauseln und Algorithmus B auf kurzen Klauseln gut ist. Allerdings betrachten wir bei MAXSAT Eingaben, in denen sowohl lange als auch kurze Klauseln vorkommen. Daher brauchen wir eine genauere Analyse. Seien nA und nB die erwartete Anzahl erfüllter Klauseln P bei Algorithmus A bzw. Algorithmus B. Wir werden zeigen, dass max{nA , nB } ≥ 34 · j ẑj gilt. Wir haben schon oben P bemerkt, dass opt ≤ j ẑj . Also folgt max{nA , nB } ≥ 34 · opt, was zu zeigen ist. P Statt max{nA , nB } ≥ 43 · j ẑj zu zeigen, beweisen wir 3 X nA + n B ≥ · ẑj . 2 4 j Dies genügt, da offensichtlich max{nA , nB } ≥ (nA + nB )/2. Sei l(C) die Länge einer Klausel C. Es ist X nA = 1 − 2−l(Cj ) ≥ Klausel Cj sowie nB ≥ X Klausel Cj X Klausel Cj 1 1− 1− l(Cj ) 68 1 − 2−l(Cj ) · ẑj , l(Cj ) ! · ẑj . Die folgende einfache Rechnung zeigt, dass für alle natürlichen Zahlen k gilt, dass k ! 1 (1 − 2−k ) + 1 − 1 − ≥ 3/2, k woraus die Behauptung folgt. Für k = 1 und k = 2 ergibt die Summe den Wert 3/2, für k ≥ 3 ist 1 − 2−k ≥ 7/8 und 1 − (1 − k1 )k ≥ 1 − 1/e; damit ist die Summe mindestens 7/8 + 1 − 1/e ≥ 3/2. 2 7.4 Derandomisierung mit der Methode der bedingten Wahrscheinlichkeiten Die Algorithmen A und B wählen den Wert der Variablen xi mit einer Wahrscheinlichkeit pi mit 1 und mit Wahrscheinlichkeit 1 − pi mit 0, wobei die Werte aller Variablen unabhängig gezogen werden. Wir wollen in diesem Abschnitt einen Ansatz vorstellen, mit dem derartige Algorithmen durch deterministische Algorithmen ersetzt werden können, wobei der Wert der berechneten Lösung mindestens dem erwarteten Wert der Lösung des gegebenen randomisierten Algorithmus entspricht. Diese Umwandlung eines randomisierten Algorithmus in einen deterministischen Algorithmus nennt man Derandomisierung. Im Allgemeinen ist der derandomisierte Algorithmus weniger effizient als der gegebene randomisierte Algorithmus. Während aber der randomisierte Algorithmus ein gutes Ergebnis nur mit einer bestimmten Wahrscheinlichkeit liefert, liefert der derandomisierte Algorithmus mit Sicherheit ein gutes Ergebnis. Die Methode der bedingten Wahrscheinlichkeiten geht folgendermaßen vor: Für jedes Zufallsbit, das der randomisierte Algorithmus anfordert, wird deterministisch berechnet, welche der beiden Wahlen die bessere“ ist, und diese wird dann gewählt. Dies ist natürlich ” nicht immer effizient möglich. Bei den Algorithmen A und B führt dieser Ansatz aber zu einem effizienten deterministischen Algorithmus. Wir beschreiben das Verfahren zunächst noch allgemein: Sei ein randomisierter Algorithmus Arand gegeben. Wir gehen im Folgenden immer davon aus, dass Arand nur legale Lösungen für das betrachtete Optimierungsproblem berechnet. Der Wert der Lösung ist eine Zufallsvariable Z. Der spezielle Wert hängt dabei von den gezogenen Zufallsbits y1 , . . . , ym ab. Wir können einen deterministischen Algorithmus Adet konstruieren, indem wir geeignete Belegungen d1 , . . . , dm für die Zufallsbits deterministisch berechnen. Zunächst sollte klar sein, dass eine solche Wahl überhaupt existiert. Da der Erwartungswert der Lösungen, die Arand berechnet, E[Z] ist, muss es eine Folge d1 , . . . , dm von Belegungen der Zufallsvariablen y1 , . . . , ym geben, so dass Arand damit eine Lösung mit Wert mindestens E[Z] berechnet. Bei der Methode der bedingten Wahrscheinlichkeiten werden die deterministischen Bits sukzessive in dieser Reihenfolge gewählt. Nachdem d1 , . . . , di , i < m, gewählt worden sind, bestimmen wir das so genannte Gewicht dieser (partiellen) Belegung, das wir mit w(d1 , . . . , di ) bezeichnen. Dies ist definiert als die bedingte Erwartung des Wertes einer 69 Lösung, wenn y1 = d1 , . . . , yi = di und wenn man die restlichen Bits yi+1 , . . . , ym zufällig wählt. Formal: w(d1 , . . . , di ) = E[Z | y1 = d1 , . . . , yi = di ]. Insbesondere gilt: w(∅) = E[Z] und w(d1 , . . . , dm ) = E[Z | y1 = d1 , . . . , ym = dm ]. Wenn nun w(·) effizient berechnet werden kann, so kann man effizient testen, welche Wahl des nächsten Bits di eine bessere Lösung erwarten lässt, di = 0 oder di = 1. Man wählt dann die vielversprechendere. Insgesamt erhalten wir den folgenden Algorithmus Adet : For i = 1 to m do if w(d1 , . . . , di−1 , 0) > w(d1 , ..., di−1 , 1) then di := 0 else di := 1 Lasse Arand laufen, aber ersetze die Zufallsbits y1 , . . . , ym durch d1 , . . . , dm . Lemma 7.4.1: Algorithmus Adet liefert eine Lösung mit Wert mindestens E[Z]. Beweis: w(d1 , . . . , di ) = E[Z | y1 = d1 , . . . , yi = di ] = (1 − pi+1 )E[Z | y1 = d1 , . . . , yi = di , yi+1 = 0] + pi+1 E[Z | y1 = d1 , . . . , yi = di , yi+1 = 1] = (1 − pi+1 ) · w(d1 , . . . , di , 0) + pi+1 · w(d1 , . . . , di , 1). Das heißt, w(d1 , . . . , di ) ist ein gewichtetes Mittel von w(d1 , . . . , di , 0) und w(d1 , . . . , di , 1). Daraus folgt w(d1 , . . . , di ) ≤ max{w(d1 , . . . , di , 0), w(d1 , . . . , di , 1)}. Da wir den Wert von di+1 so wählen, dass w(d1 , . . . , di , di+1 ) maximal ist, folgt insbesondere E[Z] = w(∅) ≤ w(d1 ) ≤ w(d1 , d2 ) ≤ · · · ≤ w(d1 , . . . , dm ). Dabei ist w(d1 , . . . , dm ) der Wert der Lösung, die Adet berechnet, also gilt die Behauptung. 2 Die Algorithmen A und B erfüllen alle genannten Voraussetzungen an Arand . Also genügt es, für die Derandomisierung anzugeben, wie wir die Gewicht w(d1 , . . . , di ) effizient berechnen können. Sei wieder Xl die Zufallsvariable, die den Wert 0 annimmt, wenn die l-te Klausel nicht erfüllt ist, und 1 sonst. Es gilt wieder Z = X1 + · · · + Xm und wegen der Linearität des Erwartungswertes E[Z] = E[X1 ] + · · · + E[Xm ]. Diese Gleichung gilt auch für die bedingten Erwartungswerte, d.h., E[Z | y1 = d1 , . . . , yi = di ] = E[X1 | y1 = d1 , . . . , yi = di ] + · · · + E[Xm | y1 = d1 , . . . , yi = di ]. Um das Gewicht w(d1 , . . . , di ) = E[Z | y1 = d1 , . . . , yi = di ] auszurechnen, genügt es also, die Erwartungswerte E[Xl | y1 = d1 , . . . , yi = di ] zu berechnen. Wir unterscheiden drei Fälle. 70 1) Cl ≡ 0, d.h., alle Literale in Cl sind durch die Belegung y1 = d1 , . . . , yi = di mit 0 belegt. Dann gilt E[Xl | y1 = d1 , . . . , yi = di ] = Prob[Xl = 1 | y1 = d1 , . . . , yi = di ] = 0. 2) Cl ≡ 1, d.h., ein Literal in Cl ist durch die Belegung y1 = d1 , . . . , yi = di mit 1 belegt. Dann gilt E[Xl | y1 = d1 , . . . , yi = di ] = Prob[Xl = 1 | y1 = d1 , . . . , yi = di ] = 1. 3) Alle durch y1 = d1 , . . . , yi = di belegten Literale in Cl sind mit 0 belegt und Cl enthält noch t unbelegte Literale xi(1) , . . . , xi(t) . Dann gilt E[Xl | y1 = d1 , . . . , yi = di ] = Prob[Xl = 1 | y1 = d1 , . . . , yi = di ] = 1 − (1 − pi(1) ) · · · (1 − pi(t) ). Dies ergibt sich, weil Cl nur dann nicht erfüllt wird, wenn alle t nicht belegten Literale mit 0 belegt werden, was mit Wahrscheinlichkeit (1 − pi(1) ) · · · (1 − pi(t) ) geschieht. Für negierte Literale ist natürlich (1 − pi() ) durch pi() zu ersetzen. Insgesamt haben wir einen effizienten Algorithmus zur Berechnung von w(d1 , . . . , di ) konstruiert und erhalten damit derandomisierte Varianten sowohl von Algorithmus A als auch Algorithmus B. Die Rechenzeit ist polynomiell und wird bei Algorithmus B durch die Zeit für die Lösung des linearen Optimierungsproblems dominiert. Die Rechenzeit für die übrigen Schritte der derandomisierten Variante von Algorithmus B, sowie für die derandomisierte Variante von Algorithmus A ist linear. Wir fassen die Ergebnisse dieses Kapitels zusammen: Die Derandomisierung von Algorithmus A liefert einen deterministischen Algorithmus für MAXkSAT, der für jede Eingabe mindestens m · (1 − 1/2k ) Klauseln erfüllt. Analog liefert die Derandomisierung von Algorithmus B einen deterministischen Algorithmus, der mindestens (1 − 1/e) · opt Klauseln erfüllt, wobei opt die maximale Anzahl von gleichzeitig erfüllbaren Klauseln bezeichnet. Wenn wir beide derandomisierten Algorithmen auf eine Eingabe anwenden und das bessere Ergebnis wählen, folgt mit der Analyse aus Abschnitt 7.3, dass wir einen deterministischen Algorithmus für MAXSAT erhalten, der für jede Eingabe mindestens 3 · opt/4 Klauseln erfüllt, also einen deterministischen Approximationsalgorithmus mit Güte 4/3. 71 8 8.1 Randomisierte Suchheuristiken Motivation Bei der Lösung von Optimierungsproblemen wünschen wir uns Algorithmen, die mit geringer worst case Rechenzeit stets optimale Lösungen liefern. Dieses weitgehende Ziel wird allerdings selten erreicht, ein positives Beispiel ist die Berechnung kürzester Wege, zwei weitere Problemklassen, die auf diese Weise effizient lösbar sind, werden wir in Kapitel 9 und Kapitel 10 kennen lernen. Ansonsten haben wir bisher drei mögliche Auswege untersucht. Einerseits haben wir Algorithmen wie Branch-and-Bound-Algorithmen entworfen, die zwar die Berechnung einer optimalen Lösung garantieren, die aber bezüglich der Rechenzeit heuristisch sind. Wir können nur hoffen, dass sie auf vielen für uns interessanten Eingaben effizient sind. Mit Approximationsalgorithmen haben wir das Ziel der Berechnung optimaler Lösungen aufgegeben. Wir können gute Rechenzeiten garantieren und in vielen Fällen auch eine Schranke für die Approximationsgüte. Es lässt sich auch die Situation von garantierter Rechenzeit und nicht garantierter Approximationsgüte vorstellen. Dann hoffen wir, dass wir für viele für uns interessante Eingaben Lösungen berechnen, deren Wert nahe dem Wert optimaler Lösungen liegt. Für viele Approximationsalgorithmen erhalten wir für viele Eingaben Lösungen mit wesentlich größerer Qualität als sie durch die Abschätzung der worst case Güte garantiert wird. Der dritte Ausweg ist Randomisierung, die wir schon Kapitel 7 betrachtet haben. Die Idee besteht darin, dass es in vielen Fällen hilft, eine Entscheidung zufällig zu treffen, wenn es uns nicht möglich ist, eine gute Entscheidung zu berechnen. Ein weiteres Beispiel hierfür, bei dem wir die Güte des Ergebnisses auch bewerten können, werden wir in Kapitel 11 kennenlernen. Randomisierung hilft, wenn es genügend viele gute Optionen gibt, eine gute Option durch gezielte Suche aber nicht effizient gefunden wird. Randomisierung hilft aber auch bei der Exploration schlecht strukturierter Suchräume oder von Suchräumen, über die wir wenig wissen. Hier wollen wir randomisierte Suchheuristiken untersuchen, für die wir weder eine gute Rechenzeit noch eine gute Qualität der Lösung garantieren“ können. Wir erwarten zu” mindest, dass unsere Lösungen lokal“ optimal sind, wobei lokal fair“ definiert ist. Wenn ” ” die Nachbarschaft jedes Suchpunktes der ganze Suchraum ist, fallen die Begriffe lokal optimal und global optimal zusammen. Dennoch führen auch vernünftige Lokalitätsbegriffe zu so großen Nachbarschaften, dass es nicht mehr effizient ist, die gesamte Nachbarschaft systematisch zu durchsuchen. Beim 3-Opting für das TSP sind die Nachbarn einer Tour alle Touren, die durch das Aufbrechen der Tour in drei Teilstücke und ein beliebiges Zusammenkleben der Teile entstehen können. Damit hat die Nachbarschaft eine Größe von Θ(n3 ). In so einem Fall werden Nachbarn zufällig erzeugt. Ist der neue Suchpunkt mindestens so gut wie der alte, wird er aktueller Suchpunkt. Der Suchprozess wird gestoppt, wenn eine Zeitschranke überschritten wird, längere Zeit sich der Wert des aktuellen Suchpunktes nicht oder nur wenig verbessert hat oder ein anderes Kriterium greift. Wir können einen derartigen Suchprozess an zufälligen Suchpunkten starten oder an durch von anderen Algorithmen berechneten vermutlich guten oder sogar garantiert ziemlich guten Suchpunkten. Die Güte der erzeugten Lösung kann durch eine Multistartvariante ver72 bessert werden, bei der die randomisierte lokale Suche mehrfach unabhängig voneinander durchgeführt wird. Ein gravierender Nachteil der lokalen Suche ist, dass lokale Optima nicht verlassen werden können. Falls die betrachtete Funktion viele lokale Optima hat, von denen viele schlechte Werte haben, müssen wir es randomisierten Suchheuristiken erlauben, lokale Optima zu verlassen. Simulated-Annealing-Algorithmen akzeptieren mit im Laufe der Zeit fallender Wahrscheinlichkeit auch schlechtere Suchpunkte, während evolutionäre Algorithmen Suchoperatoren, also Operatoren zur Erzeugung neuer Suchpunkte, benutzen, bei denen jeder Suchpunkt gewählt werden kann, aber Punkte in größerer Nähe zum aktuellen Suchpunkt eine größere Wahrscheinlichkeit bekommen, gewählt zu werden. Darüber hinaus arbeiten sie mit Mengen aktueller Suchpunkte, die sich beeinflussen (Populationen). Dies erlaubt Suchoperatoren, die neue Suchpunkte aus mehreren aktuellen Suchpunkten konstruieren (Kreuzungen, Crossover). Wir wollen in diesem Kapitel am Beispiel evolutionärer Algorithmen untersuchen, ob und wie das Verhalten randomisierter Suchheuristiken analysiert werden kann. Zunächst wollen wir zwei Szenarien für den Einsatz randomisierter Suchheuristiken unterscheiden. Wir können sie auf gut strukturierte Probleme anwenden, für die die bekannten Methoden versagen oder keine befriedigenden Lösungen berechnen. In diesem Fall sollte die Wahl der Suchoperatoren problemspezifisch sein. Es ist nicht sinnvoll, Strukturwissen über das Problem nicht einzusetzen. Hier gibt es eine Vielzahl problemspezifischer randomisierter Suchheuristiken, deren Analyse auch durch die problemspezifischen Besonderheiten kompliziert wird. Daneben gibt es das Szenario der Black-Box-Optimierung. Dabei ist der endliche Suchraum S gegeben, die zu optimierende Funktion f : S → + 0 aber unbekannt. Wir können Wissen über f nur durch Stichproben (sampling) gewinnen. Eine randomisierte Suchheuristik kann den ersten Suchpunkt x1 nach einer frei zu wählenden Verteilung zufällig bestimmen. Allgemein sind bei der Wahl des t-ten Suchpunktes xt die ersten t − 1 Suchpunkte x1 , . . . , xt−1 und ihre Funktionswerte f (x1 ), . . . , f (xt−1 ) (oft Fitnesswerte genannt) bekannt und die Wahrscheinlichkeitsverteilung, nach der xt gewählt wird, kann auf dieses Wissen zurückgreifen. Wieso ist dieses Szenario sinnvoll? In vielen Situationen der Praxis sind nicht genügend Ressourcen (Zeit, Geld, Know-how) vorhanden, um einen problemspezifischen Algorithmus zu entwickeln. Dann ist eine robuste“ Suchheuristik gefragt, die ” auf vielen Eingaben vieler Probleme gut arbeitet. In dieser Situation darf man natürlich nicht hoffen, problemspezifische Algorithmen zu übertreffen. Es gibt auch Situationen, in denen die Einstellung freier Parameter eines technischen Systems zu optimieren ist. Diese Systeme sind so komplex, dass die Funktion f , die die Güte von Parametereinstellungen beschreibt, tatsächlich unbekannt ist. Dann lassen sich f -Werte nur experimentell (oder mit Hilfe von Simulationen) berechnen. In dieser Situation bilden randomisierte Suchheuristiken den einzigen Ausweg. Wir werden randomisierte Suchheuristiken auf folgende Weise bewerten. Es sei Xf die Zufallsvariable, die das kleinste t beschreibt, so dass xt optimal (oder fast optimal) ist. Beim Ablauf des Algorithmus können wir Xf nicht messen, da wir ja im Black-Box-Szenario 73 nicht wissen, ob ein Suchpunkt optimal ist. Dennoch ist Xf aussagekräftig. Die meisten randomisierten Suchheuristiken benutzen Stoppkriterien, die den Suchprozess recht schnell stoppen, wenn ein optimaler Suchpunkt gefunden wurde. Allerdings ist nicht ausgeschlossen, dass der Suchprozess zu früh“ gestoppt wird. Wir sind an der erwarteten ” Optimierungszeit (oder Suchzeit) E(Xf ) ebenso interessiert wie an der Erfolgswahrscheinlichkeit s(t) = Prob(Xf ≤ t). Wenn s(p1 (n)) ≥ 1/p2 (n) ist, hat eine Multistartstrategie mit h(n)p2 (n) unabhängigen, gleichzeitigen Starts mit jeweils p1 (n) Schritten eine Erfolgswahrscheinlichkeit von mindestens h(n)p2 (n) 1 ≈ 1 − e−h(n) . 1− 1− p2 (n) Exemplarisch wollen wir in diesem Kapitel einige Analysemethoden für randomisierte Suchheuristiken vorstellen. Im Mittelpunkt wird der einfachste evolutionäre Algorithmus stehen (Populationsgröße 1, nur Mutation), der so genannte (1 + 1)EA (aus einem Suchpunkt, dem Elter, wird ein neuer Suchpunkt, das Kind, erzeugt, aus beiden wird der bessere Suchpunkt als neuer aktueller Suchpunkt gewählt). Algorithmus 8.1.1 ((1 + 1)EA für die Maximierung von Funktionen f : {0, 1}n → ): 1.) Wähle x ∈ {0, 1}n gemäß der Gleichverteilung. 2.) Wiederhole den folgenden Mutationsschritt (bis ein Stoppkriterium greift): Es sei x0 = (x01 , . . . , x0n ), wobei alle x0i unabhängig voneinander gebildet werden und Prob(x0i = 1 − xi ) = 1/n (das i-te Bit flippt) und Prob(x0i = xi ) = 1 − 1/n ist. Es wird x genau dann durch x0 ersetzt, wenn f (x0 ) ≥ f (x) ist. Die Wahl von 1/n als Mutationswahrscheinlichkeit ist die Standardwahl. Im Durchschnitt ändert sich ein Bit. Bei kleineren Werten passiert in vielen Schritten nichts und bei größeren Werten ist es schwierig, optimale Suchpunkte genau zu treffen. Allerdings gibt es Probleme, bei denen andere Werte (oder sogar wechselnde Werte) für die Mutationswahrscheinlichkeit besser sind. In Kapitel 8.2 beschreiben wir eine einfache Analysetechnik für den (1 + 1)EA und wenden sie auf mehrere Probleme an. Danach zeigen wir in Kapitel 8.3 den Einsatz von Potentialfunktionen bei der Analyse des (1 + 1)EA. In Kapitel 8.4 diskutieren wir die Unterschiede beim Einsatz größerer Populationen und beim Einsatz von Multistartvarianten des (1 + 1)EA. Abschließend stellen wir in Kapitel 8.5 eine Funktion vor, bei der nur der Einsatz des Crossoveroperators zu einer polynomiellen erwarteten Optimierungszeit führt. 8.2 Eine allgemeine obere Schranke für die erwartete Optimierungszeit des (1 + 1)EA Im Folgenden nutzen wir aus, dass beim (1 + 1)EA der Fitnesswert des jeweils aktuellen Suchpunktes nie fällt. 74 Definition 8.2.1: Für A, B ⊆ {0, 1}n gilt A <f B, wenn f (x) < f (y) für alle x ∈ A und y ∈ B gilt. Eine f -basierte Partition von {0, 1}n ist eine Partition A1 , . . . , Am mit A1 <f A2 <f · · · <f Am , so dass Am genau die Suchpunkte mit maximalem f -Wert enthält. Es sei s(a) für a ∈ Ai , i < m, die Wahrscheinlichkeit, dass aus a durch Mutation ein Suchpunkt b ∈ Aj , j > i, entsteht und s(i) = min{s(x)|x ∈ Ai }. Lemma 8.2.2: Für jede f -basierte Partition A1 , . . . , Am gilt X E(Xf ) ≤ 1 + p(Ai ) s(i)−1 + · · · + s(m − 1)−1 ≤ 1 + s(1)−1 + · · · + s(m − 1)−1 , 1≤i≤m−1 wobei p(Ai ) die Wahrscheinlichkeit ist, dass die Suche mit einem Suchpunkt aus A i beginnt. Beweis: Die zweite Ungleichung ist offensichtlich richtig. In der ersten Ungleichung steht der Summand 1 für den ersten Suchpunkt. Allgemein gilt nach dem Satz von der vollständigen Wahrscheinlichkeit X E(Xf ) = p(Ai )E(Xf |x1 ∈ Ai ), 1≤i≤m wobei x1 den zufälligen ersten Suchpunkt bezeichnet. Wenn x1 ∈ Ai und i < m ist, muss durch Mutation aus einem Suchpunkt aus Ai ein Suchpunkt aus Ai+1 ∪· · ·∪Am entstehen. Solange der aktuelle Suchpunkt in Ai ist, ist diese Erfolgswahrscheinlichkeit mindestens s(i) und daher die erwartete Wartezeit durch 1/s(i) beschränkt. Wenn wir in Ai starten, müssen wir jede der Mengen Ai , . . . , Am−1 maximal einmal verlassen. 2 Diese sehr einfache Schranke ist in erstaunlich vielen Fällen sehr gut, obwohl wir so tun, als könnten wir nicht von Ai direkt nach Aj mit j − i > 1 springen. Allerdings muss dazu die f -basierte Partition geeignet gewählt werden. Wir beginnen mit zwei einfachen Anwendungen. Satz 8.2.3: Es sei f (x) = x1 + · · · + xn (oft ONEMAX genannt), dann gilt für den (1 + 1)EA: E(Xf ) ≤ 1 + en(ln n + 1). Beweis: Die Menge Ai soll alle x mit f (x) = i enthalten. Dann ist A0 , . . . , An eine f basierte Partition. Es sei x ∈ Ai . Dann gibt es n − i 1-Bit-Mutationen, die zu einem neuen Suchpunkt aus Ai+1 führen. Jede 1-Bit-Mutation hat eine Wahrscheinlichkeit von 1 n−1 1 1 1 − ≥ en . Also gilt n n n−i s(i) ≥ en und X 1 E(Xf ) ≤ 1 + en ≤ 1 + en(ln n + 1). n − i 0≤i≤n−1 2 75 Definition 8.2.4: Eine Funktion f : {0, 1}n → heißt unimodal, wenn es für jeden nicht optimalen Suchpunkt einen (bezüglich des Hammingabstands) benachbarten Suchpunkt mit größerem f -Wert gibt. (Meistens wird zusätzlich gefordert, dass es nur einen global optimalen Punkt gibt, aber wir können unser Ergebnis auch unter dieser abgeschwächten Forderung beweisen.) Satz 8.2.5: Es sei f eine unimodale Funktion, die I(f ) verschiedene Funktionswerte annimmt. Dann gilt für den (1 + 1)EA: E(Xf ) ≤ 1 + en(I(f ) − 1). Beweis: Wir betrachten die f -basierte Partition mit I(f ) Mengen, für die alle Suchpunkte in Ai denselben Funktionswert haben. Dann ist für alle Ai , die nicht die optimalen Suchpunkte enthalten, s(i) ≥ 1/(en), da es nach Definition mindestens eine 1-Bit-Mutation gibt, die den Funktionswert vergrößert. Nun folgt der Satz aus Lemma 8.2.2, da wir maximal I(f ) − 1 viele Ai -Mengen verlassen müssen. 2 ONEMAX ist eine lineare Funktion, die unimodal ist. Aus Satz 8.2.5 erhalten wir aber nur die schlechtere Schranke O(n2 ), da wir nicht berücksichtigen, dass es für viele Ai viele gute 1-Bit-Mutationen gibt. Wir betrachten nun den allgemeinen Fall linearer Funktionen (eigentlich affiner Funktionen): f (x) = w0 + w1 x1 + · · · + wn xn . Wenn wir xi durch 1−xi ersetzen, ändert sich die erwartete Optimierungszeit nicht, da der (1 + 1)EA Nullen und Einsen gleich behandelt. Daher können wir annehmen, dass alle Gewichte wi , i ≥ 1, nicht negativ sind. Außerdem können wir w0 = 0 annehmen, da konstante additive Terme den (1 + 1)EA nicht beeinflussen. Nun ist es leicht zu sehen, dass lineare Funktionen unimodal sind. Allerdings kann I(f ) sehr groß sein, für wi = 2n−i (x wird als Binärzahl bewertet, daher BV = binary value genannt) erhalten wir den größtmöglichen Wert 2n . Aus Lemma 8.2.2 folgt dann die wenig nützliche Schranke O(n2n ). Wir können mit einer besseren f -basierten Partition eine wesentlich bessere Schranke zeigen. Satz 8.2.6: Für lineare Funktionen f und den (1 + 1)EA gilt E(Xf ) ≤ 1 + en2 . Beweis: Nach Umnummerierung der Variablen können wir annehmen, dass w1 ≥ · · · ≥ wn ≥ 0 ist. Außerdem können wir uns auf den Fall w0 = 0 beschränken. Dann definieren wir für i ∈ {1, . . . , n + 1} und wn+1 := 1 Ai = {a|w1 + · · · + wi−1 ≤ f (a) < w1 + · · · + wi }. Für a ∈ Ai gibt es ein j ≤ i mit aj = 0. Da wj ≥ wi ist, erzeugt die 1-Bit-Mutation, die nur das j-te Bit flippt, einen Suchpunkt aus Ai+1 ∪ · · · ∪ An+1 . Also ist s(i) ≥ 1/en und E(Xf ) ≤ 1 + en2 . 2 Polynome mit durch k beschränktem Grad sind Summen von Termen wxi1 xi2 · · · xij mit j ≤ k. 76 Satz 8.2.7: Für Polynome f mit durch k beschränktem Grad und N Termen, deren Gewichte alle positiv sind, und den (1 + 1)EA gilt E(Xf ) ≤ 1 + eN nk . Beweis: Wir gehen wie im Beweis von Satz 8.2.6 vor und sortieren die Terme nach fallenden Gewichten w1 ≥ · · · ≥ wN > 0. Die Ai -Mengen, 1 ≤ i ≤ N + 1, werden auf gleiche Weise gebildet. Für a ∈ Ai gibt es ein j ≤ i, so dass der zu wj gehörige Term den Wert 0 hat. Es gibt dann eine m-Bit-Mutation, m ≤ k, die genau die Nullen in dem betrachteten Term zu Einsen macht. Sie erhöht den Funktionswert, da sie diesen Term aktiviert“. Da kein Gewicht negativ ist, liefert kein anderer Term weniger als zuvor. Die ” 1 m 1 n−m 1 m Wahrscheinlichkeit für die betrachtete Mutation beträgt n 1− n ≥ n /e ≥ 1/(enk ). Daher ist 1/s(i) ≤ enk und die Behauptung folgt. 2 In den Beweisen von Satz 8.2.6 und 8.2.7 haben wir nicht berücksichtigt, dass wir unter Umständen mehr günstige Terme haben, deren Aktivierung dazu dient, Ai zu verlassen. In der Tat kann die Schranke in Satz 8.2.6 für lineare Funktionen auf O(n log n) gesenkt werden (siehe für einen Spezialfall Kapitel 8.3). Außerdem haben wir im Beweis von Satz 8.2.7 m durch k abgeschätzt und damit etwas verschenkt. Mit verbesserten Beweismethoden kann die Schranke aus Satz 8.2.7 für k ≤ log n auf O(N n2k /k) gesenkt werden. 8.3 Der Einsatz von Potentialfunktionen bei der Analyse des (1 + 1)EA In diesem Abschnitt wollen wir exemplarisch den (1 + 1)EA auf der linearen Funktion BVn (x) := 2n−1 x1 + 2n−2 x2 + · · · + 20 xn analysieren. Das Besondere an dieser Funktion ist, dass das vorderste flippende Bit entscheidet, ob der neue Suchpunkt akzeptiert wird. Dies macht den Optimierungsprozess im Vergleich zu allgemeinen linearen Funktionen überschaubar. Ein einmal entstandener 1-Block am Anfang bleibt bestehen. Andererseits kann die Anzahl der Nullen und damit der Hammingabstand zum Optimum zunehmen, im Extremfall wird 01n−1 durch 10n−1 ersetzt. Allerdings ist die erwartete Anzahl flippender Bits nur 1. Bei einem akzeptierten, den Funktionswert erhöhenden Schritt (im Weiteren erfolgreicher Schritt genannt) ist klar, dass das vorderste flippende Bit von 0 zu 1 wechselt. Wenn dieses an Position < 1. Wenn diese erwartete k steht, ist die erwartete Anzahl weiterer flippender Bits n−k n Anzahl durch eine Konstante c < 1 beschränkt wäre, würden wir in jedem erfolgreichen Schritt durchschnittlich 1 − c Einsen gewinnen und würden erwarten, dass O(n) erfolgreiche Schritte ausreichen, und müssten nur noch“ die erwartete Anzahl erfolgloser Schritte ” abschätzen. Dies ist die grundlegende Idee der folgenden Analyse. In der Analyse betrachten wir eine so genannte Irrfahrt, wobei der englische Begriff random walk vielleicht etwas anschaulicher ist. Eine Irrfahrt ist ein stochastischer Prozess auf einer Menge von Punkten, wobei ein Startpunkt, Übergangswahrscheinlichkeiten und ein Stoppkriterium gegeben sind. In unserem Fall ist die Punktmenge die Menge der ganzzahligen Punkte auf der Zahlengeraden und der Startpunkt ist 0. Bei der Irrfahrt kann man 77 in jedem Schritt vom dem jeweils erreichten Punkt (eine oder mehrere) Positionen nach links oder rechts gehen oder auch an der erreichten Position bleiben. Die Irrfahrt wird beendet, wenn der Punkt 1 erreicht wird. Die Aufgabe wird darin bestehen, die erwartete Dauer der Irrfahrt nach oben abzuschätzen. Satz 8.3.1: Die erwartete Optimierungszeit des (1 + 1)EA auf BVn ist O(n log n). Beweis: Zur Vereinfachung nehmen wir an, dass n gerade ist. Wir zerlegen den Optimierungsprozess in zwei Phasen, die erste der Länge Xf1 endet, wenn zum ersten Mal der Suchpunkt mit n/2 Einsen beginnt, und die zweite der Länge Xf2 endet mit dem Finden des Optimums. Also ist Xf = Xf1 + Xf2 . Der Vorteil dieser Aufteilung besteht darin, dass wir bei der Analyse der ersten Phase den hinteren Teil ignorieren können. Damit ist die erwartete Anzahl flippender Bits nur 1/2. Die zweite Phase lässt sich analog analysieren, wobei alle Schritte, in denen ein Bit in der ersten Hälfte flippt, erfolglos sind. Daher konzentrieren wir uns zunächst auf die erste Phase, die wir weiter unterteilen. Es sei Ui die Zufallsvariable, die den ersten Zeitpunkt beschreibt, zu dem die erste Hälfte (des aktuellen Suchpunktes) mindestens i Einsen enthält. Also wählen wir die Anzahl der Einsen in der ersten Hälfte als Potentialfunktion. Insbesondere ist U0 = 1. Es folgt Xf1 = Un/2 = U0 + (U1 − U0 ) + (U2 − U1 ) + · · · + (Un/2 − Un/2−1 ). Um E(Ui −Ui−1 ) abzuschätzen, betrachten wir die zufällige Anzahl Yi erfolgreicher Schritte und die zufällige Anzahl Zi erfolgloser Schritte im Zeitintervall (Ui−1 , Ui ]. Wir erinnern, dass bei erfolgreichen Schritten das vorderste geflippte Bit von 0 nach 1 wechselt. Weiterhin gilt Ui − Ui−1 = Yi + Zi . Der entscheidende Teil des Beweises besteht im Nachweis der Behauptung E(Yi ) ≤ 2. Es sei x0 der aktuelle Suchpunkt zum Zeitpunkt Ui−1 . Es sei x0 , x1 , x2 , . . . die Folge der durch erfolgreiche Schritte erzeugten Suchpunkte und Dj die Zufallsvariable, die die Differenz der Anzahl an Einsen in xj und xj−1 misst. Entweder hat bereits x0 mindestens i Einsen, dann ist Yi = 0, oder Yi ist die kleinste Anzahl t erfolgreicher Mutationen mit D1 +· · · +Dt ≥ 1. Die Wahrscheinlichkeitsverteilung von Dj unter der Annahme D1 + · · · + Dj−1 < 1 hat die folgenden Eigenschaften. Der Suchpunkt xj−1 hat höchstens i − 1 Einsen (anderenfalls wäre schon der Zeitpunkt Ui erreicht). Da ein erfolgreicher Schritt betrachtet wird, steht an der linkesten Stelle p, die verändert wird, eine 0. Diese Position trägt 1 zu Dj bei. Rechts von Position p wird jedes Bit unabhängig mit Wahrscheinlichkeit 1/n geflippt. Wir machen nun die pessimistische Annahme, dass alle diese Veränderungen von Bits Wechsel von 1 nach 0 sind. Die Anzahl der auf diese Weise veränderten Bits bezeichnen wir mit Bj . Dann folgt Dj ≥ 1 − Bj bzw. Bj ≥ 1 − Dj . Wir schätzen nun Bj mit Hilfe einer weiteren Zufallsvariablen Bj∗ ab. Die Verteilung von Bj∗ ist die Binomialverteilung mit n/2 − 1 Versuchen und Erfolgswahrscheinlichkeit 1/n. Da wir die Zahl der Versuche gegenüber Bj überschätzen, ist Bj ≤ Bj∗ und damit Dj ≥ Dj∗ := 1 − Bj∗ . Hieraus folgt weiterhin Prob(Bj∗ ≤ r) ≤ Prob(Bj ≤ r) für alle r und damit auch Prob(Dj∗ ≤ r) ≥ Prob(Dj ≤ r). Wir beobachten außerdem, dass durch die 78 Abschätzungen die Verteilung von Bj∗ sowohl von der Anzahl der Einsen im betrachteten Suchpunkt und damit von i als auch von j unabhängig ist. Es sei Yi∗ die kleinste Zahl t mit D1∗ + · · · + Dt∗ ≥ 1. Wegen Dj∗ ≤ Dj folgt Yi∗ ≥ Yi und damit E(Yi∗ ) ≥ E(Yi ). Der Vorteil besteht nun darin, dass auch Yi∗ von i unabhängig ist. Wir bezeichnen daher Yi∗ mit Y ∗ . Es genügt nun, E(Y ∗ ) ≤ 2 zu zeigen. Nach Definition ist Dj∗ ≤ 1 und E(Dj∗ ) = 1 − n/2−1 > 1/2. Es sei St = D1∗ + · · · + Dt∗ . Die n Zufallsvariablen S0 = 0, S1 , S2 , . . . beschreiben eine Irrfahrt auf der Zahlengeraden: Der zum Zeitpunkt j erreichte Punkt (=Wert von Sj ) auf der Zahlengeraden gibt eine untere Schranke für die Differenz der Anzahl der Einsen in xj und x0 an, wobei die Abschätzung darin besteht, dass wir mit Dj∗ statt Dj arbeiten. Die Irrfahrt beginnt an Punkt 0. Wir beenden die Irrfahrt, wenn wir zum ersten Mal den Punkt 1 erreichen; der Stoppzeitpunkt wird dann durch die Zufallsvariable Y ∗ beschrieben. Da Dj∗ ≤ 1, erreichen wir auf keinen Fall einen Punkt rechts von der 1. Durch die Abschätzung von Dj durch Dj∗ haben wir erreicht, dass die Irrfahrt zeithomogen (Sr − Sr−1 = St − St−1 ) und ortshomogen (die Verteilung von Sr − Sr−1 hängt nicht von Sr−1 ab) ist. Die Zeithomogenität folgt aus Sr − Sr−1 = Dr∗ = 1 − Br∗ = 1 − Bt∗ = Dt∗ = St − St−1 , die Ortshomogenität daraus, dass Sr − Sr−1 = Dr∗ = 1 − Br∗ von r und damit von Sr−1 unabhängig ist. Um den Erwartungswert E(Y ∗ ) abzuschätzen, leiten wir eine Rekursionsformel für E(Y ∗ ) her und lösen diese auf. Wir interpretieren E(Y ∗ ) als die erwartete Zeit um bei der Irrfahrt um 1 nach rechts zu gehen. Aufgrund der Zeit- und Ortshomogenität können wir die Irrfahrt nach dem ersten Schritt wie folgt zerlegen. Der erste Schritt trägt zu E(Y ∗ ) genau 1 bei. Wir erreichen mit Wahrscheinlichkeit Prob(D1∗ = d) den Punkt d, wobei wegen Dj∗ ≤ 1 gilt, dass d ≤ 1 ist. Bei Start in Position d beträgt der Abstand bis zum Punkt 1 genau 1 − d. Die erwartete Restzeit bis zum Stoppen bei Start in Position d ist dann (1 − d)E(Y ∗ ). Insbesondere ist diese Zeit 0 für d = 1. Wir erhalten also die folgende Rekursionsgleichung: X E(Y ∗ ) = 1 + Prob(D1∗ = d)(1 − d)E(Y ∗ ) d≤1 = 1 + E(Y ∗ ) − E(Y ∗ ) X Prob(D1∗ = d)d d≤1 ∗ ∗ = 1 + E(Y ) − E(Y ) · E(D1∗ ). Durch Auflösen nach E(Y ∗ ) erhalten wir die so genannte waldsche Identität E(Y ∗ ) = 1/E(D1∗ ). Da wir oben gezeigt haben, dass E(Dj∗ ) > 1/2 ist, erhalten wir E(Y ∗ ) < 2 und damit E(Yi ) < 2. Während des Zeitintervalls bis Ui ist die Anzahl der Einsen in der ersten Hälfte des Suchpunktes höchstens i − 1. Also gibt es mindestens n/2 − (i − 1) erfolgreiche 1-BitMutationen. Damit beträgt die Wahrscheinlichkeit für einen erfolgreichen Schritt stets mindestens n/2−1 1 1 n n 1 −i+1 ≥ −i+1 e−1/2 =: p̂. 1− 2 n n 2 n 79 Die durchschnittliche Zeit, bis wir k erfolgreiche Schritte haben, ist somit durch k/p̂ beschränkt. Also ist E(Ui − Ui−1 ) = E(Yi + Zi ) X = Prob(Yi = k)E(Yi + Zi |Yi = k) k ≤ X Prob(Yi = k)k p̂−1 = E(Yi ) · p̂−1 ≤ 2 · k ne1/2 . n/2 − i + 1 Schließlich ist E(Xf1 ) ≤ 1 + X E(Ui − Ui−1 ) 1≤i≤n/2 1 1 ≤ 1 + 2ne 1 + + ··· + 2 n/2 n ≤ 1 + 2ne1/2 ln + 1 = O(n log n). 2 1/2 Die Analyse für die zweite Phase verläuft analog. Allerdings ist die erwartete Anzahl von Schritten mit (1 − n1 )−n/2 ≤ e1/2 zu multiplizieren, da wir im Durchschnitt höchstens e1/2 Schritte darauf warten müssen, dass in der ersten Hälfte kein Bit flippt. Nur derartige Schritte haben die Chance, akzeptierte Änderungen zu bewirken. 2 8.4 Populationen oder Multistarts? Wir untersuchen zwei Erweiterungen des (1 + 1)EA. Bei der Variante mit Populationen speichert der Algorithmus zu jedem Zeitpunkt mehrere Suchpunkte, von denen er einen zufällig auswählt und darauf Mutationen durchführt. Wenn der neu erhaltene Suchpunkt mindestens so gut ist wie ein schlechtester Suchpunkt der Population, wird ein solcher schlechtester Suchpunkt durch den neuen ersetzt. Bei der Multistart-Variante wird dagegen der (1 + 1)EA (mit jeweils einem Suchpunkt) mehrfach gestartet und es wird das beste Ergebnis von allen Iterationen ausgewählt. Die in der Überschrift gestellte Frage hat natürlich keine eindeutige Antwort. Wir wollen hier nur an einem Beispiel verdeutlichen, warum Multistarts so erfolgversprechend sind. Außerdem werden wir sehen, dass die Mischung aus positiven und negativen Gewichten bereits bei Polynomen vom Grad 2 zu Problemen führt, die es bei nur positiven Gewichten nicht gibt. Für eine reelle Zahl α > 1 und |x| als Abkürzung für x1 + · · · + xn sei CHn (y, x1 , . . . , xn ) = y · α · |x| + (1 − y)((α + 1)n/2 − |x|) (CH=Choice). Der Name rührt daher, dass wir mit y eine der linearen Funktionen α|x| (für y = 1) und (α + 1)n/2 − |x| (für y = 0) auswählen. Beide linearen Funktionen haben für |x| = n/2 denselben Funktionswert αn/2, eine ist mit |x| monoton wachsend und die andere monoton fallend. Da α > 1 ist, ist (1, 1, . . . , 1) einziges globales Optimum 80 mit Wert αn, während (0, 0, . . . , 0) lokales Optimum mit Wert (α + 1)n/2 ist. (Es ist (α + 1)n/2 < αn ⇔ α + 1 < 2α ⇔ 1 < α.) Wir wollen folgenden Satz zeigen. Satz 8.4.1: Für jedes ε > 0 gibt es eine Konstante c(ε), so dass der (1 + 1)EA die Funktion CHn mit einer Wahrscheinlichkeit von mindestens 1/2 − ε in c(ε)n log n Schritten optimiert. Für jedes ε > 0 beträgt die Erfolgswahrscheinlichkeit des (1 + 1)EA nach 2o(n log n) Schritten höchstens 1/2 + ε. Das folgende Lemma zeigt, dass sich der potentielle Erfolg des (1 + 1)EA auf CHn in den ersten Schritten entscheidet. Lemma 8.4.2: Jeweils mit einer Wahrscheinlichkeit von 1/2 − o(1) hat der (1 + 1)EA nach n3/4 Schritten einen Suchpunkt (y, x) mit y = 1 und |x| ≥ n/2 + n2/3 oder einen Suchpunkt (y, x) mit y = 0 und |x| ≤ n/2 − n2/3 erzeugt. Beweis: Für den ersten Suchpunkt ist y = 1 und y = 0 jeweils mit Wahrscheinlichkeit 1/2. Außerdem gilt nach der Chernoff-Schranke mit Wahrscheinlichkeit 1 − o(1) (∗) n/2 − n2/3 ≤ |x| ≤ n/2 + n2/3 . Die Wahrscheinlichkeit, dass y in einem Schritt flippt, beträgt 1/(n+1) (da der Suchraum {0, 1}n+1 ist, beträgt die Mutationswahrscheinlichkeit 1/(n+1)). Daher beträgt die Wahrscheinlichkeit, dass y in keinem von n3/4 Schritten flippt, mindestens 1 − n3/4 /(n + 1) = 1 − o(1). All diese Ereignisse sind unabhängig. Daher gilt jeweils mit Wahrscheinlichkeit 1/2 − o(1), dass y mit dem Wert 0 (oder 1) startet, ihn während n3/4 Schritten nicht verändert und (∗) für den ersten Suchpunkt erfüllt ist. Unter diesen Bedingungen arbeitet der (1 + 1)EA während der ersten n3/4 Schritte wie auf ONEMAX (oder −ONEMAX) mit dem nicht wesentlichen Unterschied, dass die Mutationswahrscheinlichkeit 1/(n + 1) beträgt. Wir betrachten den Fall ONEMAX. Solange |x| ≤ n/2 + n2/3 ist, gibt es mindestens n/2 − n2/3 gute“ 1-Bit-Mutationen, die die Anzahl der Einsen ” erhöhen. Damit ist die Wahrscheinlichkeit, die Anzahl der Einsen in einem Schritt zu 1 und damit durch eine positive Konstante c nach unerhöhen, durch (n/2 − n2/3 )e−1 · n+1 ten beschränkt. Eine weitere Anwendung der Chernoff-Schranke zeigt, dass wir mit einer Wahrscheinlichkeit von 1 − o(1) mindestens 2n2/3 Erfolge in n3/4 unabhängigen Versuchen mit Erfolgswahrscheinlichkeit c haben. Da wir angenommen haben, dass (∗) erfüllt ist, folgt die Behauptung. 2 Beweis von Satz 8.4.1: Wir zerlegen den Ablauf des (1+1)EA in die ersten n3/4 Schritte und die restlichen Schritte. Nach Lemma 8.4.2 gilt nach n3/4 Schritten mit Wahrscheinlichkeit 1/2 − o(1), dass y = 1 und |x| ≥ n/2 + n2/3 gilt. Wenn wir kurzfristig annehmen, dass y sich nicht ändert, betrachten wir ONEMAX und die erwartete Optimierungszeit lässt sich für eine Konstante c durch cn log n abschätzen. Die Misserfolgswahrscheinlichkeit nach rcn log n Schritten beträgt nach der markoffschen Ungleichung höchsten 1/r. Nun gibt es aber eventuell Schritte, in denen y flippt. Um einen derartigen Schritt zu 81 akzeptieren, müssten, da |x| ≥ n/2 + n2/3 ist, mindestens n2/3 x-Bits von 1 auf 0 flippen. Die Wahrscheinlichkeit, dass mindestens k = n2/3 Bits flippen, ist beschränkt durch (Stirling-Formel) k 1 1 n 2/3 = 2−Ω(n log n) . ≤ k n+1 k! Dass so etwas in polynomiell vielen Schritten passiert, hat ebenfalls exponentiell kleine Wahrscheinlichkeit. Nun wählen wir r groß genug, so dass die Summe der Fehlerwahrscheinlichkeiten 1 1 1 2/3 + o(1) + + 2−Ω(n log n) ≤ + ε 2 r 2 ist. Dann ist c(ε) := rc+1 geeignet, wobei der Summand +1 für die ersten n3/4 ≤ 1·n log n Schritte steht. Die zweite Behauptung folgt nahezu auf gleiche Weise. Mit einer Wahrscheinlichkeit von mindestens 21 − ε wird in O(n log n) Schritten das lokale Optimum in (0, 0, . . . , 0) erreicht. Danach müssen für einen erfolgreichen Schritt mindestens n/2 x-Bits und das y-Bit flip1 = 2−Ω(n log n) . 2 pen. Die Wahrscheinlichkeit dafür ist für einen Schritt höchstens (n/2)! Wir wollen nun den Unterschied zwischen der Multistartstrategie und der Variante mit Populationen diskutieren. Wenn wir nun einen Multistart mit h(n) unabhängigen (1+1)EAs laufen lassen, ist die Wahrscheinlichkeit, dass keiner der (1+1)EAs in c(ε)n log n Schritten erfolgreich ist, höchstens (1/2 + ε)h(n) . Wenn wir dagegen mit Populationen von h(n) unabhängigen Suchpunkten starten, ist es zwar sehr unwahrscheinlich (Wahrscheinlichkeit (1/2)h(n) ), dass für alle Suchpunkte y = 0 gilt. Wenn α > 1 nahe bei 1 liegt, kann die zufällige Streuung der Funktionswerte bewirken, dass die Suchpunkte mit y = 0 (etwas) höhere Funktionswerte haben. Durch den so genannten Hitchhiking-Effekt“ führt die ” größere Chance, Suchpunkte mit höheren Funktionswerten für die nächste Generation zu übernehmen, eventuell dazu, dass die Suchpunkte mit y = 0 die Suchpunkte mit y = 1 verdrängen. Es ist zumindest nicht klar, ob wir die gleiche Erfolgswahrscheinlichkeit wie bei der Multistartstrategie erreichen. Beim Einsatz von Populationen besteht stets die Gefahr, dass die Population nicht genügend Diversität behält und wir viele Suchpunkte verwalten, aber fast nur das Verhalten eines Suchpunktes simulieren. 8.5 Kreuzungen können die erwartete Optimierungszeit drastisch senken Kreuzungsoperatoren (Crossover-Operatoren) gelten als prägendes Merkmal evolutionärer Algorithmen. Sie ahmen das Crossover von Chromosomen in der Genetik nach, indem aus zwei Suchpunkten durch einen geeigneten Operator (Beispiele s.u.) ein neuer Suchpunkt konstruiert wird. Ihr erfolgreicher Einsatz setzt natürlich eine Population mit genügend Diversität voraus. Manche Experimente zeigen den Nutzen von Kreuzungsoperatoren. Bis vor kurzem konnte dieser Nutzen aber nicht theoretisch untermauert werden. Es gab kein Beispiel von Funktionen, für die bewiesen werden konnte, dass die erwartete 82 Optimierungszeit eines typischen evolutionären Algorithmus mit Kreuzungsoperator wesentlich geringer als die erwartete Optimierungszeit von evolutionären Algorithmen ohne Kreuzungsoperator ist. Es gab sogar die kuriose Situation, dass für Funktionen (genannt Royal-Road-Funktionen), die speziell so entworfen waren, dass der Kreuzungsoperator hilfreich sein soll, experimentell nachgewiesen wurde, dass evolutionäre Algorithmen ohne Kreuzungsoperator effizienter sind. Wir werden hier für eine Funktion den Nutzen von Kreuzungsoperatoren beweisen. Definition 8.5.1: Es seien x, y ∈ {0, 1}n . Das zufällige Ergebnis z einer Ein-PunktKreuzung ist der Suchpunkt z = (x1 , . . . , xi , yi+1 , . . . , yn ), wobei i ∈ {1, . . . , n − 1} gleichverteilt gewählt wird. Das zufällige Ergebnis u einer gleichmäßigen Kreuzung ist der Suchpunkt u = (u1 , . . . , un ), wobei die einzelnen uj unabhängig bestimmt werden, uj = xj ist, falls xj = yj ist, und ansonsten uj jeden der Werte xj und yj , also jeden der Werte 0 und 1, mit Wahrscheinlichkeit 1/2 annimmt. Bei Ein-Punkt-Kreuzungen wird implizit angenommen, dass benachbarte Positionen eine engere Beziehung haben als entferntere Positionen. Der folgende Algorithmus wird steady-state genetischer Algorithmus genannt, genetisch deshalb, weil Kreuzungen wesentlicher Bestandteil sind, und steady-state deshalb, weil nur ein neuer Suchpunkt pro Schritt erzeugt wird. Der neue Suchpunkt wird zur Population hinzugefügt und dann wird entschieden, welcher Suchpunkt aus der Population entfernt wird. Die Populationsgröße verändert sich also im Lauf des Algorithmus nicht. Populationen sind Multimengen, in denen Suchpunkte mehrfach vorkommen können. Die im folgenden Algorithmus gewählten Parameter sind Standardwerte. Außerdem enthält der Algorithmus eine einfache Methode, um eine gewisse Diversität zu garantieren. Algorithmus 8.5.2: Die Kreuzungsrate c ist eine Konstante 0 < c < 1. 1.) Die Anfangspopulation besteht aus n unabhängig und zufällig gewählten Suchpunkten aus {0, 1}n . 2.) Mit Wahrscheinlichkeit c wird Schritt 30 ausgeführt, ansonsten Schritt 300 . 30 .) Wähle zwei Suchpunkte x und y aus der aktuellen Population. Es sei z ∗ das Ergebnis einer Ein-Punkt-Kreuzung von x und y und z das Ergebnis einer Mutation von z ∗ . 300 .) Wähle einen Suchpunkt x aus der aktuellen Population. Es sei z das Ergebnis einer Mutation von x. 4.) Wir fügen z zur Population hinzu und betrachten in der Population nur die Suchpunkte mit minimalem f -Wert und unter denen nur diejenigen, die die meisten Duplikate in der Population haben. Von diesen Suchpunkten wird einer zufällig gewählt und aus der Population entfernt. Fahre bei Schritt 2 fort, bis ein Stoppkriterium greift. 83 Wir haben in Schritt 30 und 300 die Selektionsprozedur offen gelassen. Hier gibt es viele Möglichkeiten. Für unsere Zwecke ist es ausreichend zu fordern, dass die Wahrscheinlichkeit, x zu wählen, mindestens so groß ist wie die Wahrscheinlichkeit, x0 zu wählen, wenn f (x) ≥ f (x0 ) ist. Wir kommen nun zu den Funktionen Rn,m , m ≤ dn/3e, die auch Real-Royal-Road-Funktionen genannt werden. Definition 8.5.3: Es sei b(a) die Länge des längsten 1-Blocks in a, also das größte k mit ai = ai+1 = · · · = ai+k−1 = 1 für ein i. Dann ist 2 2n Rn,m (a) := n|a| + b(a) 0 falls a = (1, 1, . . . , 1) falls |a| ≤ n − m sonst Diese Funktion ist für eine evolutionäre Suche, die auf Mutationen beruht, schwer. Die Wahrscheinlichkeit für zufällig gewählte Suchpunkte höchstens n − m Einsen zu haben, beträgt 1 − 2−Ω(n) (Chernoff-Schranke). Also werden außer (1, 1, . . . , 1) keine Suchpunkte mit mehr als n−m Einsen akzeptiert. Für Mutationswahrscheinlichkeiten p(n) < 1/2 (der spezielle Fall 1/n ist dabei mit erfasst) wird die Wahrscheinlichkeit, aus einem Suchpunkt mit höchstens n − m Einsen (1, 1, . . . , 1) zu erzeugen, für Suchpunkte mit genau n − m Einsen maximiert und beträgt dann pm (1−p)n−m . Dies ist maximal für p = m und beträgt n m n−m −n m dann m (n − m) n ≤ (m/n) . Also ist die erwartete Suchzeit mindestens (n/m)m . Für m = dn/3e ist dies 2Ω(n) . Satz 8.5.4: Die erwartete Optimierungszeit des steady-state genetischen Algorithmus 8.5.2 für Rn,m ist durch O(n3 m) beschränkt. Beweis: Wir zerlegen den Optimierungsprozess in Phasen mit vorgegebenen Zielen und berechnen die erwartete Länge jeder dieser Phasen. Phase 1 Ziel: Mindestens ein Suchpunkt enthält höchstens n − m Einsen oder ist optimal. Erwartete Zeit: 1 + o(1). Beweis: Die Wahrscheinlichkeit, dass die Anfangspopulation die gewünschten Eigen2 schaften nicht hat, beträgt 2−Ω(n ) (Chernoff-Schranke). Die Wahrscheinlichkeit, dass Mutation einen Suchpunkt mit der gewünschten Eigenschaft erzeugt, ist viel größer als n−n = 2−n log n . Also beträgt im schlechten Fall die Wartezeit höchstens nn = 2n log n . 2 Damit ist die erwartete Wartezeit nn 2−Ω(n ) = o(1). 2 Phase 2 Voraussetzung: Phase 1 ist erfolgreich abgeschlossen. Ziel: Alle Suchpunkte enthalten genau n − m Einsen oder ein Suchpunkt ist optimal. Erwartete Zeit: O(n3 /m). 84 Beweis: Wir nehmen pessimistisch an, dass wir das Optimum nicht finden. Nach Definition der Funktion ist es wichtiger, die Anzahl der Einsen zu erhöhen, als die Länge von 1Blöcken. Wir zeigen, dass, wenn das Ziel noch nicht erreicht ist, die Wahrscheinlichkeit, die Anzahl der Einsen zu erhöhen, mindestens (1 − c)m/(en) beträgt und damit die erwartete Wartezeit O(n/m). Da wir maximal n(n − m) Einsen erzeugen müssen, folgt die Behauptung. Mit Wahrscheinlichkeit 1 − c wird nur Mutation angewendet. Wenn wir dafür einen Suchpunkt mit weniger als n − m Einsen wählen, gibt es m gute“ Ein-Bit-Mutationen ” und eine Erfolgswahrscheinlichkeit von mindestens m(1/n)(1 − 1/n)n−1 ≥ m/(en). Wenn wir jedoch einen Suchpunkt mit genau n − m Einsen zur Mutation auswählen, beträgt die Wahrscheinlichkeit, eine Replikation zu erzeugen, (1 − 1/n)n ≥ e−1 (1 − 1/n) ≥ m(1 − 1/n)/(en). Hierbei haben wir m/n ≤ 1/3 ≤ e−1 benutzt. Diese Replikation verdrängt einen Suchpunkt mit weniger Einsen. 2 Phase 3 Voraussetzung: Phase 2 ist erfolgreich abgeschlossen. Ziel: Alle Suchpunkte x der aktuellen Population haben genau n − m Einsen, die alle einen Block bilden, oder ein Suchpunkt ist optimal. Erwartete Zeit: O(n2 log n). Beweis: Wir nehmen pessimistisch an, dass wir das Optimum nicht finden. Dann werden nur Suchpunkte mit genau n − m Einsen akzeptiert. Es seien b1 ≤ · · · ≤ bn die aktuellen Längen der längsten 1-Blöcke der Suchpunkte. Suchpunkte werden auch nur durch solche ersetzt, die einen mindestens so großen b-Wert haben. Also sind b1 und b1 + · · · + bn im Lauf der Zeit nicht fallend. Es genügt die Schritte zu betrachten, die nur Mutationen durchführen. Falls b1 = · · · = bn = i, wird einer dieser Suchpunkte ausgewählt. Es gibt mindestens eine 0-Position, deren Änderung zu 1 den 1-Block verlängern würde. Um die Anzahl der Einsen konstant zu halten, müsste eine der n − m − i Einsen, die nicht zu dem Block gehören, flippen. Es gibt also mindestens n − m − i gute“ 2-Bit-Mutationen, von denen ” jede eine Wahrscheinlichkeit von mindestens 1/(en2 ) hat. Die erwartete Wartezeit beträgt O(n2 /(n − m − i)). Da dieses Warten für jedes i nur höchstens einmal auftritt, beträgt die erwartete Gesamtwartezeit O(n2 log n). Falls b1 = i ist und j > 0 Suchpunkte einen größeren b-Wert haben, erhöhen wir die Summe der b-Werte, wenn wir einen dieser j Suchpunkte wählen (Mindestwahrscheinlichkeit j/n) und eine Replikation erzeugen. Die erwartete Wartezeit beträgt also O(n/j). Dies für festes i über alle j summiert ergibt O(n log n). Da es weniger als n Werte für i gibt, erhalten wir auch hier einen Gesamtbeitrag von O(n2 log n). 2 Phase 4 Voraussetzung: Phase 3 ist erfolgreich abgeschlossen. Ziel: Die aktuelle Population enthält jeden der m+1 verschiedenen Suchpunkte mit n−m Einsen und Blocklänge n − m mindestens einmal oder ein Suchpunkt ist optimal. Erwartete Zeit: O(n3 m). 85 Beweis: Wir nehmen pessimistisch an, dass wir das Optimum nicht finden. Wir nennen nun einen Suchpunkt vom Typ j, wenn sein 1-Block an Position j beginnt. Falls diese Phase nicht beendet ist, gibt es ein j, so dass die aktuelle Population einen Suchpunkt vom Typ j enthält und keinen vom Typ j − 1 oder keinen vom Typ j + 1. In jedem Fall wird der Suchpunkt vom Typ j mit Wahrscheinlichkeit (1 − c)/n zur alleinigen Mutation ausgewählt. Dies liegt daran, dass in dieser Phase alle Suchpunkte denselben Funktionswert haben. Es gibt eine gute 2-Bit-Mutation, die den 1-Block um eine Position in die gewünschte Richtung verschiebt. Also beträgt die erwartete Wartezeit, um die Anzahl verschiedener Suchpunkte um 1 zu erhöhen, O(n3 ). Hierbei geht ein, dass wir einen Suchpunkt eliminieren, der doppelt vorhanden ist. Dieser Mechanismus reicht also aus, um Diversität zu erzeugen. Die Behauptung folgt, da wir von einer Anzahl verschiedener Suchpunkte von mindestens 1 auf eine Anzahl von m+1 kommen müssen. Für diese Phase ist es notwendig, dass die Populationsgröße mindestens m + 1 beträgt. 2 Phase 5 Voraussetzung: Phase 4 ist erfolgreich abgeschlossen. Ziel: Ein Suchpunkt ist optimal. Erwartete Zeit: O(n2 ). Beweis: Nur in dieser Phase spielt der Kreuzungsoperator eine Rolle. Wir haben in der Population jeweils mindestens einen Suchpunkt vom Typ 1, also 1n−m 0m , und mindestens einen Suchpunkt vom Typ m+1, also 0m 1n−m . Die Wahrscheinlichkeit, ein solches Paar (x vom Typ 1, y vom Typ m + 1) zur Kreuzung auszuwählen, beträgt mindestens c/n2 . Jede Schnittposition i mit m ≤ i ≤ n − m führt bei der Ein-Punkt-Kreuzung zur Erzeugung von z ∗ = 1n . Die Wahrscheinlichkeit einer solchen Schnittposition beträgt (n − 2m + 1)/(n − 1) ≥ 1/3. Die Wahrscheinlichkeit, dass auch z = 1n ist, beträgt mindestens 1/e. Insgesamt ist die Erfolgswahrscheinlichkeit jedes Schrittes Ω(1/n2 ). 2 Der Satz folgt, indem wir die erwarteten Wartezeiten aufsummieren. 2 An dieser Stelle stellt sich die Frage, was gleichmäßige Kreuzungen für die betrachtete Funktion leisten. Die Analyse der ersten vier Phasen bleibt unverändert. Auch hier kann 1n nur aus 1n−m 0m und 0m 1n−m entstehen, wobei die Reihenfolge dieser beiden Suchpunkte im (x, y)-Paar irrelevant ist. Allerdings beträgt die Erfolgswahrscheinlichkeit der gleichmäßigen Kreuzung nur 2−2m . Während durch Ein-Punkt-Kreuzungen die erwartete Optimierungszeit für m = dn/3e von exponentiell auf polynomiell fällt, erhalten wir bei gleichmäßigen Kreuzungen nur für m = O(log n) polynomielle erwartete Optimierungszeiten, wobei dann die erwartete Optimierungszeit ohne Kreuzungen nicht exponentiell, sondern nur superpolynomiell ist. Es gibt jedoch kompliziertere Funktionen, bei denen die erwartete Optimierungszeit für gleichmäßige Kreuzungen von exponentiell auf polynomiell fällt. 86 9 Maximierung von Flüssen in Netzwerken 9.1 Problemstellung und Beispiele Das Problem der Maximierung von Flüssen in Netzwerken ist ein zentrales Problem, das in seiner Grundform und mit verschiedensten Erweiterungen viele Anwendungen gerade in Situationen hat, die auf den ersten Blick nicht wie Flussprobleme aussehen. Zur Einführung des Problems stellen wir uns vor, dass wir in einem Verkehrsnetz den Verkehrsstrom von Q (der Quelle) zu S (der Senke) maximieren wollen. Wir modellieren Kreuzungen und Abzweigungen als Knoten. Diese werden durch gerichtete Kanten verbunden, wenn zwischen ihnen eine Straße in der gegebenen Richtung existiert und befahrbar ist. Jede Kante hat eine Kapazität, die den maximalen Verkehrsstrom (Fahrzeuge pro Stunde) beschreibt. Es soll nun der Verkehrsstrom von s nach t unter Einhaltung der Kapazitätsschranken (implizit ist natürlich 0 eine untere Schranke für den Verkehrsstrom auf jeder Kante) maximiert werden. Es ist später manchmal hilfreich, wenn der betrachtete Graph asymmetrisch ist, also für Kanten (u, v) die Kanten (v, u) nicht existieren. Dies widerspricht natürlich dem Beispiel, in dem die meisten Straßen in beiden Richtungen befahren werden können. Dann können wir aber auf der Kante (v, u) einen Knoten w einführen, wobei nur die Kanten (v, w) und (w, u) den Knoten w berühren. Wenn wir den neuen Kanten dieselbe Kapazität wie (v, u) geben und dann (v, u) entfernen, erhalten wir ein äquivalentes Problem. Auf diese Weise können alle Doppelkanten entfernt werden (siehe Abbildung 9.1.1). c u c v u c0 v c0 w c0 Abbildung 9.1.1: Vermeidung von Doppelkanten“. ” Dennoch müssen wir festhalten, dass das kanonische Problem der Verkehrsoptimierung in der reinen Form unrealistisch ist, da typischerweise nicht alle Fahrzeuge von Q nach S wollen. Wir formalisieren das Problem. Definition 9.1.1: Ein Netzwerk besteht aus einem gerichteten asymmetrischen Graphen G = (V, E) mit zwei ausgezeichneten Knoten Q (Quelle) und S (Senke) und einer Kapazitätsfunktion c : E → 0 . Dabei führt keine Kante zu Q und keine Kante verlässt S. Definition 9.1.2: Es sei N = (G = (V, E), Q, S, c) ein Netzwerk. i) Eine Funktion ϕ : E → + 0 heißt Fluss auf N , wenn für alle Knoten v ∈ V − {Q, S} die Kirchhoff-Regel erfüllt ist, d.h., es gilt X X ϕ(e) = ϕ(e) e=(v,·) e=(·,v) (alles was in v ankommt, wird weitergegeben). 87 ii) Ein Fluss ϕ heißt zulässig, wenn er die Kapazitätsschranken einhält, also ϕ(e) ≤ c(e) für alle e ∈ E ist. P iii) Der Wert des Flusses ϕ beträgt w(ϕ) = ϕ(e). e=(Q,·) iv) Der Fluss ϕ heißt maximal, wenn ϕ zulässig ist und w(ϕ0 ) ≤ w(ϕ) für alle zulässigen Flüsse ϕ0 gilt. Da nach der Kirchhoff-Regel kein Knoten v ∈ V − {Q, S} Fluss verschluckt“ oder er” P ” zeugt“, kommt der an Q erzeugte Fluss bei S an und es gilt w(ϕ) = e=(·,S) ϕ(e). Es mag überraschen, dass wir nur ganzzahlige Kapazitätsschranken erlauben, aber der Fluss reellwertig sein darf. Bei rationalen Kapazitätsschranken können wir ohne weiteres erst alle Kapazitätsschranken mit dem Hauptnenner H multiplizieren. Dann erhalten wir ein ganzzahliges Problem. Wenn wir bei der Lösung den Fluss auf jeder Kante wieder durch H dividieren, erhalten wir offensichtlich eine Lösung des ursprünglichen Problems (ähnlich wie beim TSP oder beim Rucksackproblem). Beliebige reellwertige Kapazitätsschranken führen jedoch zu erheblichen Problemen. Allerdings erlauben wir eigentlich nie“ reellwer” tige Eingaben (wie sollen wir die Eingabelänge messen?). Bei vielen Problemen sollte die Lösung ganzzahlig sein. Diese Forderung kann die Komplexität eines Problems erheblich vergrößern, vergleiche die Komplexitäten von linearer Optimierung und ganzzahliger Optimierung. Dies ist hier nicht der Fall. Unsere Algorithmen werden stets optimale Flüsse liefern, die ganzzahlig sind. Dies muss jedoch nicht für alle denkbaren Algorithmen gelten. Das in Abbildung 9.1.2 dargestellte Problem hat viele maximale Lösungen. Für jede Zahl a ∈ [0, 1] ist ϕa (Q, u) = 1, ϕa (u, v) = ϕa (v, S) = a und ϕa (u, w) = ϕa (w, S) = 1 − a offensichtlich maximal. Nur für a = 0 und a = 1 ist der Fluss ganzzahlig. 1 Q 1 v 1 u S 1 w 1 Abbildung 9.1.2: Ein Netzwerk mit Kapazitätswerten. Wir stellen nun zwei Beispiele vor. Das erste Beispiel ist die Berechnung maximaler Matchings in bipartiten Graphen. Es sei G∗ = (U ∗ ∪V ∗ , E ∗ ) ein bipartiter Graph, bei dem alle Knoten zwischen U ∗ und V ∗ verlaufen. Die Aufgabe besteht darin, eine möglichst große Menge von Kanten zu wählen, die keinen Knoten gemeinsam haben. Ein Matching ist also eine eindeutige Zuordnung zwischen einer Teilmenge der Knoten in U ∗ und einer Teilmenge der Knoten in V ∗ . Wir werden dieses Problem genauer in Kapitel 10 untersuchen. Hier betrachten wir die Berechnung maximaler Matchings mit Hilfe von Flussalgorithmen. Dazu definieren wir ein Netzwerk N wie folgt. Die Knotenmenge ist V = U ∗ ∪V ∗ ∪{Q, S}, wobei Q und S neue Knoten sind. Die Kantenmenge E enthält Kanten von Q zu jedem 88 Knoten in U ∗ , von jedem Knoten in V ∗ zu S und die Kanten aus E ∗ werden vom Knoten in U ∗ zum Knoten in V ∗ gerichtet. Alle Kanten haben Kapazität 1. Abbildung 9.1.2 zeigt ein kleines Beispiel mit U ∗ = {u} und V ∗ = {v, w}. Allgemein sei U ∗ = {u1 , . . . , uk } und V ∗ = {v1 , . . . , vn }. Die Beziehung zwischen maximalen Matchings und maximalen Flüssen ist leicht herzustellen. Satz 9.1.3: Wenn G∗ ein Matching der Größe g hat, gibt es auf N einen ganzzahligen Fluss mit Wert g. Wenn ϕ ein ganzzahliger Fluss auf N mit Wert g ist, kann ein bipartites Matching der Größe g auf G∗ in linearer Zeit berechnet werden. Beweis: Der zu einem Matching gehörige Fluss hat den Wert 1 genau auf allen Kanten (u∗i , vj∗ ), die zum Matching gehören, auf (Q, u∗i ), wenn u∗i auf einer Matchingkante liegt, und auf (vj∗ , S), wenn vj∗ auf einer Matchingkante liegt. Der Fluss g wird auf den kantendisjunkten Wegen (Q, u∗i , vj∗ , S) für (u∗i , vj∗ ) ∈ M transportiert. Ein ganzzahliger Fluss kann bei Kapazitätswerten von 1 nur die Werte 0 und 1 auf den einzelnen Kanten haben. Wir wählen alle Kanten (u∗i , vj∗ ), auf denen der gegebene Fluss den Wert 1 hat. Dies ist ein Matching. Es kann nicht zwei Kanten (u∗i , vj∗ ) und (u∗i , vl∗ ) mit Fluss 1 geben, da nur Fluss 1 von Q nach u∗i fließen kann, analog für die V ∗ -Knoten. Außerdem müssen g Kanten von U ∗ nach V ∗ Fluss 1 transportieren, da nur so Fluss g von Q nach S transportiert werden kann. 2 Satz 9.1.3 impliziert insbesondere, dass ein optimaler ganzzahliger Fluss zu einem optimalen bipartiten Matching führt. Das zweite Problem ist das Meisterschaftsproblem. Wir betrachten eine Meisterschaftsliga in einer Sportart, bei der die Regeln einen Sieger erzwingen (Volleyball, Baseball, . . . ). Der Sieger eines Spiels erhält einen Punkt, der Verlierer keinen Punkt. Im Verlaufe der Saison habe jedes Team A schon pA Punkte gesammelt und die Fans von Team A∗ fragen sich, ob ihr Team wenigstens noch theoretisch Meister werden kann. Dazu kann optimistischerweise angenommen werden, dass Team A∗ alle ausstehenden Spiele gewinnt. Es sei p∗ die dann von Team A∗ erreichte Punktzahl. Wir nehmen an, dass Team A∗ dann Meister wird, wenn kein anderes Team mehr als p∗ Punkte erhält (das Team A∗ gewinnt die restliche Spiele so ” hoch“, dass es bei Punktgleichheit Meister wird). Dummerweise spielen die Konkurrenten gegeneinander und es können nicht alle immer verlieren. Zu dieser Situation konstruieren wir folgendes Netzwerk N . Der zugrunde liegende Graph erhält die Knoten Q und S, für jedes noch ausstehende Spiel, an dem Team A∗ nicht beteiligt ist, einen Spielknoten und für jedes Team außer A∗ einen Teamknoten. Von Q geht zu jedem Spielknoten eine Kante mit Kapazität 1. Vom Spielknoten für die Begegnung der Teams A und B geht je eine Kante mit Kapazität 1 zu den Teams A und B. Vom Teamknoten A geht eine Kante zu S, deren Kapazität die für A noch erlaubten Punkte“ angibt, also p∗ − pA . Der ” Fluss auf diesem Netzwerk ist durch die Anzahl a ausstehender Spiele, an denen A∗ nicht beteiligt ist, beschränkt, da mehr Fluss nicht die Quelle verlassen kann. Wir behaupten, dass A∗ noch genau dann Meister werden kann, wenn der maximale Fluss auf N den Wert a hat. Wir können unsere Diskussion nach den Vorbereitungen auf ganzzahlige Flüsse beschränken. 89 Wenn A∗ noch Meister werden kann, benutzen wir die zugehörigen Spielergebnisse, um einen Fluss mit Wert a zu konstruieren. Alle Q verlassenden Kanten erhalten Fluss 1. Die Spielknoten geben den Fluss 1 an den Gewinner des Spiels weiter. Der Teamknoten für A sammelt also die zusätzlich zu pA von A gewonnenen Punkte. All diese Punkte muss der Teamknoten an S weitergeben. Da A∗ Meister würde, erhält A höchstens p∗ − pA Punkte und der so konstruierte Fluss ist zulässig. Sei nun ein ganzzahliger Fluss mit Wert a gegeben. Dann erhalten alle Spielknoten den Fluss 1. Die Spielknoten müssen diesen Fluss an eines der beteiligten Teams weitergeben. Dieses Team erklären wir zum Sieger des zugehörigen Spiels. Damit erhält der Teamknoten für A so viel Fluss, wie A noch ausstehende Spiele gewinnt. Da der gegebene Fluss zulässig ist, kann A diesen Fluss an S weitergeben. Das impliziert wegen der Kapazitätsschranke, dass A nicht mehr als p∗ − pA Spiele gewonnen hat und unsere Entscheidung, wer welches Spiel gewinnt, A∗ zum Meister macht. Beim Handball (und früher beim Fußball) gibt es die Zwei-Punkte-Regel (zwei Punkte für Sieg, ein Punkt für Unentschieden, null Punkte bei Niederlage). Dies bereitet uns keine Probleme. Ein Spiel zwischen A und B kann durch zwei Spiele zwischen A und B mit der Baseballregel ersetzt werden, so dass wir die oben behandelte Situation erhalten. Dieser Trick gelingt nicht bei der Drei-Punkte-Regel (drei Punkte für Sieg, ein Punkt für Unentschieden, null Punkte bei Niederlage), da nun die in einem Spiel vergebene Gesamtpunktzahl nicht mehr vom Spielausgang unabhängig ist. Das Meisterschaftsproblem wird dann NP-äquivalent. Wir haben gesehen, dass Probleme als Flussprobleme modelliert werden können, die gar nicht so aussehen. Es ist also an der Zeit, Algorithmen zur Lösung von Flussproblemen zu beschreiben. In den nächsten vier Unterkapiteln stellen wir vier immer besser werdende Algorithmen vor, die aufeinander aufbauen. 9.2 Der Algorithmus von Ford und Fulkerson Alle betrachteten Algorithmen basieren auf lokalen Verbesserungen. Der folgende DFSAlgorithmus mit Laufzeit O(|V | + |E|) testet, ob eine lokale Verbesserung möglich ist. In den beiden darauf folgenden Lemmas zeigen wir, wie wir gegebenenfalls den Fluss vergrößern können und dass der Fluss maximal ist, wenn keine lokale Verbesserung möglich ist. Danach diskutieren wir die Laufzeit des Algorithmus, der auf diesen lokalen Verbesserungen beruht. Algorithmus 9.2.1: Sei ϕ ein zulässiger Fluss. Zu Beginn seien alle Knoten unmarkiert. Knoten können nur aufgrund der folgenden drei Regeln markiert werden. Die Reihenfolge, in der die Regeln angewendet werden, ist unerheblich. Regel 1: Q kann markiert werden. Regel 2: Falls u markiert, v unmarkiert, e = (u, v) ∈ E und ϕ(e) < c(e), kann v mit +u“ markiert werden, e wird Vorwärtskante. ” Regel 3: Falls u unmarkiert, v markiert, e = (u, v) ∈ E und ϕ(e) > 0, kann u mit −v“ markiert werden, e wird Rückwärtskante. ” 90 Lemma 9.2.2: Wenn Algorithmus 9.2.1 den Knoten S markiert, ist ϕ nicht maximal. Ein zulässiger Fluss ϕ0 mit w(ϕ0 ) > w(ϕ) kann dann in O(|V |) Schritten berechnet werden. Beweis: Wir wollen einen Weg von Q nach S konstruieren, der aus Vorwärts- und Rückwärtskanten besteht. Wir starten am Knoten S. Allgemein sind wir an einem markierten Knoten w. An w ist der Grund für seine Markierung, also +u“ oder −v“, vermerkt. Wir ” ” suchen diesen markierten Knoten auf. Da u bzw. v zeitlich vor w markiert wurde, erreichen wir schließlich den Knoten Q, der als erster Knoten markiert werden muss. Wir erhalten die Knotenfolge Q = a0 , a1 , . . . , ak−1 , ak = S, wobei ai+1 mit +ai“ oder −ai“ markiert ” ” wurde. Im ersten Fall ist ei = (ai , ai+1 ) Vorwärtskante, im zweiten Fall ist ei = (ai+1 , ai ) Rückwärtskante. Wir berechnen δ als Minimum aller ϕ(ei ) für die Rückwärtskanten ei und aller c(ei ) − ϕ(ei ) für die Vorwärtskanten ei . Nach Konstruktion ist δ > 0. Es sei ϕ0 (e) = ϕ(e), falls e 6∈ {e0 , . . . , ek−1 }, 0 ϕ (e) = ϕ(e) + δ, falls e ∈ {e0 , . . . , ek−1 } Vorwärtskante, ϕ0 (e) = ϕ(e) − δ, falls e ∈ {e0 , . . . , ek−1 } Rückwärtskante. Nach Konstruktion ist 0 ≤ ϕ0 (e) ≤ c(e). Wir überprüfen die Kirchhoff-Regel. Sie könnte nur an einem Knoten ai , 1 ≤ i ≤ k − 1 verletzt sein. Der Knoten ai wird von ei−1 und ei berührt. Dies sind die einzigen Kanten, die ai berühren und einen anderen ϕ0 -Wert als ϕ-Wert haben. Es gibt vier Fälle abhängig davon, ob ei−1 bzw. ei Vorwärts- oder Rückwärtskante ist. +δ +δ 1.) Vor/Vor: ×−→×−→×. Der Fluss durch ai erhöht sich um δ. ai−1 ai ai+1 −δ −δ 2.) Rück/Rück: ×←−×←−×. Der Fluss durch ai verringert sich um δ. +δ −δ 3.) Vor/Rück: ×−→×←−×. Der Fluss durch ai bleibt gleich groß. −δ +δ 4.) Rück/Vor: ×←−×−→×. Der Fluss durch ai bleibt gleich groß. In jedem Fall ist (Q, a1 ) Vorwärtskante, und w(ϕ0 ) = w(ϕ) + δ. Die Rechenzeit für dieses Vorgehen ist O(|V |). 2 Der Weg a0 , a1 , . . . , ak aus Vorwärts- und Rückwärtskanten heißt flussvergrößernder Weg. Lemma 9.2.3: Wenn Algorithmus 9.2.1 den Knoten S nicht markiert, ist ϕ maximal. Beweis: Sei A die Menge der markierten Knoten. Dann ist Q ∈ A (Regel 1) und nach Voraussetzung S 6∈ A. Da die Markierungsregeln nicht mehr anwendbar sind, gilt – (x, y) ∈ E, x ∈ A, y 6∈ A ⇒ ϕ(x, y) = c(x, y), – (x, y) ∈ E, x 6∈ A, y ∈ A ⇒ ϕ(x, y) = 0. Alles, was von Q nach S fließt, muss von A nach V − A fließen. Die erste Eigenschaft sagt, dass alle Kanten von A nach V − A ausgelastet sind. Die zweite Eigenschaft sagt, dass nichts von V − A nach A zurückfließt. Für keinen anderen zulässigen Fluss ϕ0 kann mehr von A nach V − A fließen. Da außerdem alles, was für ϕ von A nach V − A fließt, auch in S ankommt, ist ϕ maximal. 2 91 Wir haben mit der Einteilung von V in A mit Q ∈ A und V − A mit S ∈ V − A einen Flaschenhals“ gefunden, der von ϕ maximal belastet wird. Kein anderer Fluss kann mehr ” durch diesen Flaschenhals schicken. Wir starten nun mit dem Fluss ϕ ≡ 0, der stets zulässig ist. Da alle c(e) P ganzzahlig sind, konstruieren wir mit Lemma 9.2.2 nur ganzzahlige Flüsse. Es sei B := e=(Q,·) c(e). Dann gilt für jeden zulässigen Fluss ϕ, dass w(ϕ) ≤ B. Spätestens nach B Flussvergrößerungen erhalten wir also einen maximalen Fluss. Satz 9.2.4: Es sei B die Summe aller c(e) mit e = (Q, ·). Maximale Flüsse können in O((|V | + |E|)B) Schritten berechnet werden. Von Q gehen maximal n − 1 Kanten aus (n = |V |), deren jeweilige Kapazität durch cmax , die maximale Kapazität aller Kanten, beschränkt ist. Also ist B ≤ (n − 1) · cmax und die Rechenzeit ist durch O(n(n+|E|)cmax ) beschränkt. Damit haben wir einen pseudopolynomiellen Algorithmus erhalten. Das Beispiel in Abbildung 9.2.1 zeigt, dass die Rechenzeit nicht polynomiell beschränkt ist. M u M 1 Q M v S M Abbildung 9.2.1: Ein Netzwerk, auf dem der Algorithmus von Ford und Fulkerson exponentielle Rechenzeit haben kann. Die Eingabelänge ist Θ(log M ). Wenn als flussvergrößernde Wege jeweils abwechselnd (Q, u, v, S) (mit Vorwärtskante (u, v)) und (Q, v, u, S) (mit Rückwärtskante (u, v)) gewählt werden, benötigen wir 2M Flussvergrößerungen zur Konstruktion eines maximalen Flusses mit Wert 2M . Die Zeit Θ(M ) ist exponentiell in der Eingabelänge Θ(log M ). Für die Darstellung der folgenden Algorithmen ist es vorteilhaft, nicht mehr zwischen den Vorwärts- und Rückwärtskanten unterscheiden zu müssen. Weiterhin wollen wir den bereits berechneten Fluss in das Netzwerk einarbeiten“, indem wir für jede Kante die ” Kapazität um den bereits vorhandenen Fluss verringern. Wir erhalten den so genannten Restgraphen. Definition 9.2.5: Gegeben ein Netzwerk G = (V, E) und eine Abbildung ϕ : E → 0 . Für eine Kante e sei rev(e) die umgedrehte Kante, also rev((x, y)) = (y, x). Der Restgraph“ Restϕ = (V, Eϕ ) hat die gleiche Knotenmenge wie G und die folgenden ” Kanten: – Für jede Kante e ∈ E mit ϕ(e) < c(e) enthält Eϕ die Kante e mit der Kapazität rϕ (e) := c(e) − ϕ(e). – Für jede Kante e ∈ E mit ϕ(e) > 0 enthält Eϕ die Kante e0 = rev(e) mit der Kapazität rϕ (e0 ) := ϕ(e). 92 Hier wird auch klar, warum es günstig ist, sich auf asymmetrische Graphen zu beschränken. Wenn das gegebene Netzwerk die Kanten (v, w) und (w, v) enthielte, könnte der Restgraph Mehrfachkanten enthalten. Das folgende Bild zeigt ein Beispiel für einen Restgraphen zu einem gegebenen Netzwerk und einem Fluss ϕ. A A B 2 1 8 2 Q 2 1 C S 7 3 8 2 0 Q B 2 0 0 2 S 3 C D 5 5 D Der Restgraph: A 6 2 2 Q B 1 2 2 2 1 S 3 3 C D 5 5 Man überzeugt sich leicht, dass die flussvergrößernden Wege des gegebenen Netzwerks (im Folgenden kurz FV-Wege) im Restgraphen gerichteten Wegen von Q nach S entsprechen, also keine Rückwärtskanten enthalten. Anstelle eines flussvergrößernden Weges können wir sogar einen Fluss auf dem Restgraphen berechnen. Diesen können wir dann zu dem bereits vorhandenen Fluss ϕ addieren“ und damit ϕ vergrößern. Wir beachten dabei, ” dass der Fluss über die für Rückwärtskanten eingefügten Kanten von ϕ zu subtrahieren ist. Es ist leicht nachzurechnen, dass wir auf diese Weise in jedem Fall einen legalen Fluss für das gegebene Netzwerk erhalten. Wir wollen an dieser Stelle anmerken, dass es einfacher ist, über den Restgraphen zu reden und sich vorzustellen, dass die Algorithmen, die man entwirft, auf dem Restgraphen operieren. Bei tatsächlichen Implementierungen und sehr grossen Netzwerken wäre es allerdings eine Verschwendung, zu gegebenem Netzwerk und Fluss den Restgraphen explizit zu konstruieren. Man geht dann so vor, dass man im Speicher nur das eigentliche Netzwerk hält und z.B. das Vorhandensein einer Kante im Restgraphen immer dann aus dem Netzwerk und dem Fluss berechnet, wenn man diese Information benötigt. 9.3 Der Algorithmus von Edmonds und Karp Wenn wir noch einmal auf das Beispiel in Abbildung 9.2.1 zurückblicken, fällt auf, dass die hohe Zahl von Flussvergrößerungen (im Folgenden kurz FVs) nicht nötig ist. Wenn wir die richtigen“ FV-Wege wählen, nämlich (Q, u, S) und dann (Q, v, S), genügen 2 ” FVs zur Berechnung des maximalen Flusses. Man kann also bei der Wahl der FV-Wege 93 Glück haben. Wir werden nun das Glück steuern und zeigen, dass bei der Wahl kürzester FV-Wege die Zahl der FVs bis zur Berechnung eines maximalen Flusses polynomiell beschränkt ist. Es genügen dann stets |E| · (n + 1) FVs. Wir beweisen zunächst ein Lemma über die Länge von Wegen in Graphen. Lemma 9.3.1: Sei G = (V, E) ein gerichteter Graph und Q ∈ V . Sei d(v) die Länge eines kürzesten Weges von Q nach v, wobei die Weglänge gleich der Anzahl der Kanten auf dem jeweiligen Weg ist. Wenn d(v) ≥ d(w) − 1 ist, bleiben durch Hinzufügen der Kante v → w alle Distanzen unverändert. Beweis: G0 bezeichne den Graph, den wir aus G nach der Veränderung bekommen und d0 bezeichne die Distanzen in G0 . Das Hinzufügen von Kanten kann prinzipiell Distanzen kleiner machen. Wir zeigen, dass dies hier nicht passieren kann. Als Erstes beobachten wir d0 (v) = d(v), da kein kürzester Weg von Q nach v in G0 die Kante v → w benutzt. Außerdem ist d0 (w) = d(w), da für jeden Weg von Q nach w in G0 , der die Kante v → w benutzt, die Länge mindestens d0 (v) + 1 = d(v) + 1 ≥ d(w) ist. Sei nun v 0 ein Knoten, der weder der Knoten v noch der Knoten w ist. Angenommen, in G0 gibt es einen Weg von Q nach v 0 , der die Kante v → w benutzt und Länge l hat. Der Teilweg von Q nach w hat Länge mindestens d(w). Wir ersetzen diesen Teilweg durch den Weg der Länge d(w), den es auch in G schon gibt. Damit haben wir auch in G einen Weg der Länge höchstens l, also ist d(v 0 ) = d0 (v 0 ). 2 Definition 9.3.2: Mit q (k) (v) wird die Länge des kürzesten Weges von Q nach v im Restgraphen Restϕk bezeichnet, wobei der nach k Flussvergrößerungen berechnete Fluss ϕk zugrunde gelegt wird und ϕ0 der Nullfluss ist. Jede Kante auf einem FV-Weg, die den Wert δ der Flussvergrößerung definiert, heißt Flaschenhals. Wir wollen stets kürzeste FV-Wege benutzen und hoffen, dass damit schnell alle kurzen FV-Wege zerstört und dann auch nicht wieder erzeugt werden. Wir nennen diese Vorgehensweise FVMIN. Lemma 9.3.3: Bei Anwendung von FVMIN gilt q (k+1) (v) ≥ q (k) (v) für alle k und v. Beweis: Wir betrachten den Restgraphen Restϕk und einen gemäß FVMIN ausgewählten kürzesten FV-Weg. Sei dies der Weg Q = v0 → v1 → · · · → vr = S. Wir geben den dabei benutzten Kanten die Namen e1 , . . . , er . Da es der kürzeste FV-Weg ist, gilt q (k) (vi ) = i. Wir haben in Lemma 9.3.1 analysiert, wie sich Distanzen verhalten, wenn man Kanten hinzufügt. Es ist klar, dass beim Entfernen von Kanten die kürzesten Wege von Q zu einem Knoten nicht kürzer werden können. Wenn man im Restgraphen entlang einer Kante ei den Fluss erhöht, dann hat dies den Effekt, dass der neu entstehende Restgraph die Kante rev(ei ) enthält und eventuell die Kante ei nicht mehr enthält. Wir stellen uns vor, dass der neue Restgraph aus dem alten Restgraphen entsteht, indem wir zunächst einige Kanten rev(ei ) hinzufügen und dann einige Kanten ei entfernen. Das Hinzufügen einer Kante rev(ei ) verändert nach Lemma 9.3.1 die Distanzen nicht, durch das anschließende Entfernen von Kanten können sich die Distanzen ebenfalls nicht verringern. 2 94 Bisher wissen wir, dass kürzeste FV-Wege nicht kürzer werden. Für eine Abschätzung der Zahl der benötigten FVs ist es jedoch wichtig, dass die kürzesten FV-Wege nach nicht zu vielen FVs länger werden. Lemma 9.3.4: Wenn bei Anwendung von FVMIN eine Kante (v, w) im Restgraphen sowohl bei der (k + 1)-ten FV als auch später bei der (m + 1)-ten FV den Flaschenhals bildet, dann ist q (m) (v) + q (m) (w) ≥ q (k) (v) + q (k) (w) + 4. Beweis: Wenn die Kante (v, w) bei der (k + 1)-ten FV der Flaschenhals war und später wieder, muss zwischendurch Fluss in der anderen Richtung von w nach v zurückgegeben worden sein. Nehmen wir an, dass dies bei der (l+1)-ten FV der Fall war (also k < l < m). Damit gilt q (l) (v) = q (l) (w) + 1. Außerdem ist q (m) (w) = q (m) (v) + 1 und q (k) (w) = q (k) (v) + 1. Da sich die Distanzen nicht verkleinern, folgt q (m) (v) ≥ q (l) (v) = q (l) (w) + 1 ≥ q (k) (w) + 1 = q (k) (v) + 2. sowie q (m) (w) = q (m) (v) + 1 ≥ q (l) (v) + 1 = q (l) (w) + 2 ≥ q (k) (w) + 2. Daraus ergibt sich die Aussage des Lemmas. 2 Satz 9.3.5: Bei Anwendung von FVMIN ist spätestens nach |E|(n + 1) Flussvergrößerungen ein maximaler Fluss berechnet. Beweis: Wenn entlang einer Kante (v, w) eine Flussvergrößerung durchgeführt wird, so ist die Distanz von Q zu v maximal n − 1 und die Distanz von Q zu w maximal n. Wenn eine Kante (v, w) bei der (m + 1)-ten FV zum i-ten Mal Flaschenhals ist, dann gilt nach Lemma 9.3.4, dass q (m) (v) + q (m) (w) ≥ 1 + 4 · (i − 1) ist. Also muss gelten 1 + (i − 1) · 4 ≤ 2n − 1, also i ≤ n+1 . 2 Jeder FV-Weg hat einen Flaschenhals. Nach mehr als |E| · (n + 1) Flussvergrößerungen gäbe es mindestens eine der maximal 2e Kanten im Restgraphen, die mehr als n+1 -mal 2 Flaschenhals war. Das kann jedoch nicht sein. 2 Hier haben wir ein Beispiel dafür, wie strukturelle Überlegungen an einem Problem zu einem effizienten Algorithmus führen. Wir müssen nur noch den Markierungsalgorithmus so verfeinern, dass er einen kürzesten FV-Weg findet. Dazu ist natürlich der BreadthFirst-Search-Ansatz (BFS) geeignet. 95 Algorithmus 9.3.6: (BFS-Markierungsalgorithmus auf dem Restgraphen) Markiere die Quelle Q. Füge den Knoten Q in eine leere Queue ein. Setze d(Q) = 0. while queue not empty do begin Entferne das oberste Element aus der Queue, nenne es v. Für jede Kante v → w im Restgraphen, bei der w nicht markiert ist: markiere w und füge w in die Queue ein. Setze d(w) = d(v) + 1. Vermerke an w, dass w von v aus erreicht wurde. end Algorithmus 9.3.6 benötigt Zeit O(n+|E|), da er eine Breitensuche implementiert. In dem Array d(x) wird nebenbei die Länge eines kürzesten Wegs von Q nach x im Restgraphen berechnet. Diese Information wird im Algorithmus von Edmonds und Karp noch nicht benötigt, aber wir haben es hier eingefügt, um zu sehen, dass man sie ohne größeren Aufwand mitberechnen kann. Da wir mit O(|E|n) Flussvergrößerungen auskommen und jede solche in Zeit O(n+|E|) = O(|E|) berechnet werden kann, erhalten wir den folgenden Satz. Satz 9.3.7: Maximale Flüsse können in Zeit O(n|E|2 ) = O(n5 ) berechnet werden. 9.4 Der Algorithmus von Dinic Im vorigen Abschnitt wurde auf effiziente Weise ein kürzester FV-Weg konstruiert. Nach dieser Flussvergrößerung wird eine neue BFS-Suche gestartet, obwohl der Fluss nur auf einem Weg verändert worden ist. Wir wollen nun alle kürzesten FV-Wege ermitteln. Dazu verwenden wir die Distanzinformationen, die der Markierungsalgorithmus 9.3.6 nebenbei ermittelt hat, um eine Datenstruktur aufzubauen, die es erlaubt, Flussvergrößerungen entlang vieler FV-Wege zugleich durchzuführen. Knoten v 6= S, die eine Distanz von mindestens d(S) von der Quelle Q haben, können nicht auf kürzesten flussvergrößernden Wegen von der Quelle zur Senke liegen und werden von der Betrachtung ausgeschlossen. Wir definieren zunächst Niveaus“ von Knoten. ” Vi := {v ∈ V | d(v) = i} für i = 0, . . . , d(S) − 1, sowie Vi = {S} für i = d(S). Wir definieren nun das so genannte Niveaunetzwerk zu einem gegebenen Fluss ϕ. Die Knotenmenge Vϕ des Niveaunetzwerks ist die Vereinigung aller Vi . Das Niveaunetzwerk enthält nur noch die Kanten aus dem Restgraphen, die zwischen benachbarten Niveaus verlaufen, d.h. Ei = {(v, w) ∈ Vi−1 × Vi | (v, w) ∈ Restϕ }. Die Kantenmenge des Niveaunetzwerks ist dann Eϕ , die Vereinigung aller Ei . Wie oben bezeichnet auch hier rϕ (e) die Kapazität der Kante e im Restgraphen. Da wir bereits beim 96 Markierungsalgorithmus angeben haben, wie die die Distanzen zur Quelle berechnet werden können, können wir diesen Algorithmus auch zur Berechnung des Niveaunetzwerkes einsetzen. Bemerkung 9.4.1: Zu einem Netzwerk G und Fluss ϕ kann das zugehörige Niveaunetzwerk in Zeit O(n + |E|) berechnet werden. Wir benötigen noch folgende Definition. Definition 9.4.2: Eine Kante e heißt saturiert bezüglich ψ, wenn ψ(e) = rϕ (e) ist. In einem Niveaunetzwerk heißt ψ Sperrfluss, wenn auf jedem Q-S-Weg mindestens eine saturierte Kante liegt. Wir werden nun zeigen, dass Sperrflüsse effizient berechenbar sind. Wir können den Sperrfluss ψ dann zu ϕ addieren“, wobei der Fluss ψ(e) für Rückwärtskanten subtrahiert wird. ” Danach zeigen wir, dass die Länge kürzester FV-Wege von Q nach S nach Addition des Sperrflusses um mindestens 1 gewachsen ist. Dies werden benutzen, um eine obere Schranke für die Zahl der Sperrflussberechnungen zu beweisen. Wir merken noch an, dass ein Sperrfluss nicht notwendigerweise ein maximaler Fluss für das Niveaunetzwerk ist; ein Beispiel zu finden, das dies erklärt, ist eine einfache Übungsaufgabe. Algorithmus 9.4.3: Konstruktion eines Sperrflusses ψ für ein Niveaunetzwerk zum Fluss ϕ im Netzwerk G. Methode: Backtracking. Der Algorithmus hat stets einen Weg Q = v0 → v1 → · · · → vi vorliegen und versucht, diesen zu einem FV-Weg zur Senke zu erweitern. 0.) ψ(e) := 0 für alle Kanten e im Niveaunetzwerk. 1.) Der aktuelle Weg ist der Weg der Länge 0 von Q nach Q, d.h., i = 0 und v0 = Q. 2.) Falls vi = S, gehe nach Schritt 3. Ansonsten durchlaufe die Adjazenzliste von vi und teste, ob es eine Kante im Niveaunetzwerk von vi zu einem Knoten w aus Vi+1 gibt. Falls ja: Verlängere den Pfad um diese Kante, d.h., i := i + 1; vi = w. Gehe zu Schritt 2. Falls nein: Die Senke kann von vi aus im Niveaunetzwerk nicht mehr erreicht werden. Entferne vi aus dem Niveaunetzwerk sowie die zu vi inzidenten Kanten. Falls i = 0 stoppe, anderenfalls setze i := i − 1 und gehe nach Schritt 2. 3.) Sei W der gefundene Weg von Q nach S und δ := min{rϕ (e) − ψ(e) | e ∈ W }. Ersetze für alle e ∈ W den Wert des Sperrflusses ψ(e) durch ψ(e) + δ. Entferne alle nun saturierten Kanten (es gibt mindestens eine) und mache in Schritt 1 weiter. Satz 9.4.4: Algorithmus 9.4.3 konstruiert in Zeit O(n · |E|) einen Sperrfluss ψ. 97 Beweis: Durch Backtracking kann jeder Q-S-Weg gefunden werden. Kanten werden in Schritt 3.) nur entfernt, wenn sie saturiert sind. Diese Kanten würden keine Vergrößerung des Sperrflusses ermöglichen. Knoten (und die damit verbundenen Kanten) werden nur entfernt, wenn von ihnen aus S nicht mehr erreicht werden kann. Also ist am Ende des Algorithmus ψ ein Sperrfluss. Nun zur Laufzeit: Jeder Versuch, einen Q-S-Weg zu konstruieren, endet mit der Entfernung einer Kante, also gibt es höchstens |E| Versuche. Q-S-Wege im Niveaunetzwerk haben Länge höchstens n−1. Daher ist nach höchstens n Schritten ein Versuch erfolgreich oder erfolglos. Somit ist die Rechenzeit durch O(|E| · n) beschränkt. 2 Wir erhalten den folgenden Algorithmus für das Flussproblem. Algorithmus 9.4.5: Berechung eines maximalen Flusses ϕ. 0.) Setze ϕ(e) := 0 für alle e ∈ E. 1.) Berechne das zum Fluss ϕ gehörige Niveaunetzwerk. Wenn dabei festgestellt wird, dass die Senke überhaupt nicht mehr von der Quelle erreicht werden kann, stoppt der Algorithmus. Der Fluss ϕ ist dann maximal. 2.) Berechne einen Sperrfluss ψ für das Niveaunetzwerk. 3.) Addiere den Sperrfluss ψ zum aktuellen Fluss ϕ. (Bei dieser Addition muss wieder die Richtung der Kante berücksichtigt werden.) Starte mit dem neuen Fluss ϕ wieder in Schritt 1. Satz 9.4.6: Mit Algorithmus 9.4.5 können maximale Flüsse in Zeit O(n2 |E|) = O(n4 ) berechnet werden. Beweis: Nach allen Vorüberlegungen ist der Algorithmus korrekt. Die Kosten für die einzelnen Schritte betragen O(|E|) für Schritt 0, O(n + |E|) für Schritt 1 (Bemerkung 9.4.1), O(n|E|) für Schritt 2 (Satz 9.4.4) und O(|E|) für Schritt 3. Es genügt also zu zeigen, dass stets n − 1 Iterationen genügen. Zu diesem Zweck schauen wir uns Wege im Restgraphen an. Wir sind gestartet mit dem Fluss ϕ. Dazu haben wir das Niveaunetzwerk konstruiert, in dem die Senke Entfernung ` von der Quelle hatte. Wir haben zu ϕ einen Sperrfluss ψ addiert“, um den Fluss ϕ0 zu ” bekommen. Wir vergleichen die beiden Restgraphen Restϕ und Restϕ0 . Kanten im Graphen Restϕ , die in einem Knoten starten, der die Entfernung x von der Quelle hat, führen zu Knoten, die eine Entfernung von höchstens x + 1 von der Quelle haben. Welche Kanten enthält Restϕ0 neu? Da wir den Fluss im Restgraphen nur entlang von Kanten erhöhen, die im Niveaunetzwerk für ϕ für irgendein j von Niveau j zu Niveau j + 1 verlaufen, enthält Restϕ0 nur solche Kanten neu, die im Niveaunetzwerk für ϕ für irgendein j von Niveau j + 1 zu Niveau j verlaufen. Damit sollte klar sein, dass Wege von der Quelle zur Senke in Restϕ0 , die mindestens eine neue Kante benutzen, Länge mindestens ` + 2 haben. 98 Wege, die keine neue Kante benutzen und Länge genau ` besitzen, sind durch den Sperrfluss zerstört worden. Also ist die Entfernung im Restgraphen Restϕ0 von der Quelle zur Senke mindestens ` + 1. 2 9.5 Ein verbesserter Algorithmus zur Sperrflussberechnung In diesem Abschnitt verbessern wir nur noch den Algorithmus für die Sperrflussberechnung und erhalten auf diese Weise einen effizienteren Algorithmus für das Flussproblem. Der vorgestellte Algorithmus wurde von Malhotra, Pramodh Kumar und Maheshvari entworfen. Gegeben sei das Niveaunetzwerk mit den Niveaus V0 = {Q}, . . . , Vr = {S}. Wir wollen wieder einen Sperrfluss konstruieren und starten mit dem Nullfluss ψ ≡ 0 in diesem Niveaunetzwerk. Diesen Fluss ψ erhöhen wir nach und nach, bis wir einen Sperrfluss erhalten. Für einen Knoten v im Niveaunetzwerk und den aktuellen Fluss ψ definiere X X (rϕ (e) − ψ(e)) . pot(v) := min (rϕ (e) − ψ(e)) , e=(·,v) e=(v,·) pot(v) gibt anschaulich den Wert an, um den man den Fluss durch den Knoten v maximal noch erhöhen kann. (pot steht daher für Potential“). ” Der folgende Algorithmus geht nun so vor, dass er einen Knoten mit minimalem pot-Wert findet und von dort ausgehend den Fluss erhöht. Dabei schiebt man den Fluss vor sich her. Im Array Überschuss[v] merkt man sich, wie viel Fluss der Knoten v noch weiterreichen muss. Man benötigt zwei Prozeduren Forward(v) und Backward(v), von denen wir nur die eine beschreiben, weil die andere spiegelsymmetrisch“ dazu ist. ” Algorithmus 9.5.1: Forward(v) Sei Überschuss[v] = pot(v) und für alle w 6= v sei Überschuss[w] = 0. Initialisiere eine Queue mit dem Knoten v. while queue not empty do begin Entferne das oberste Element aus der Queue und nenne es u. Sei e die erste von u ausgehende Kante im Niveaunetzwerk. while Überschuss[u] > 0 do begin Sei e = (u, w). Sei δ = min{rϕ (u, w) − ψ(u, w), Überschuss[u]}. Erhöhe den Fluss ψ auf der Kante (u, w) um δ und aktualisiere pot(u), pot(w). Falls Überschuss[w] = 0 ∧ δ > 0 ∧ w 6= S, füge w in die Queue ein. Überschuss[w] :=Überschuss[w] + δ. Überschuss[u] :=Überschuss[u] − δ. Falls nun rϕ (u, w) = ψ(u, w) ist, so entferne die Kante (u, w) aus dem Niveaunetzwerk. Sei e nun die nächste von u ausgehende Kante im Niveaunetzwerk. end end 99 In der Queue sollen immer alle Knoten mit positivem Überschuss stehen. Damit kein Knoten zum zweiten Mal in der Queue aufgenommen wird, testet man, ob die Erhöhung um δ den Überschuss des Knotens zum ersten Mal von 0 weg ändert und nimmt ihn nur dann in die Queue auf. Analog zur Prozedur Forward kann man sich eine Prozedur Backward(v) denken, die Fluss rückwärts“ in Richtung zur Quelle zurückgibt. ” Nun kann man zur Berechnung eines Sperrflusses wie folgt vorgehen: Algorithmus 9.5.2: (Forward-Backward-Propagation) Berechne für alle v den Wert pot(v). while Netzwerk enthält noch Q und S do begin Berechne vmin , einen Knoten mit minimalem Potentialwert. Falls pot(vmin ) > 0: Forward(vmin ), Backward(vmin ). Entferne vmin aus dem Netzwerk, aktualisiere dabei die pot(·)-Werte für die Vorgänger und Nachfolger von vmin . end Die Wahl des Knotens mit dem kleinsten Potential garantiert, dass man tatsächlich den Fluss von vmin aus zur Senke und von vmin zurück zur Quelle propagieren kann. Anschließend kann man vmin aus dem Niveaunetzwerk entfernen, weil entweder alle seine ausgehenden Kanten oder alle seine eingehenden Kanten saturiert sind, also auf allen über vmin verlaufenden Q-S-Wegen mindestens eine Kante saturiert ist. Wenn dies nach Ablauf des Algorithmus für alle Knoten gilt, ist ein Sperrfluss berechnet. Welche Laufzeit hat nun dieser Algorithmus? Die Prozedur Forward(v) hat die Laufzeit O(n + Anzahl entfernter Kanten). Der Grund dafür ist der folgende. Da jeder Knoten nur maximal einmal in die Queue aufgenommen wird, hat die äußere while-Schleife die Laufzeit O(n), wobei wir die Laufzeit der inneren Schleife vorübergehend vernachlässigen. In der inneren while-Schleife werden alle betrachteten Kanten außer der letzten aus dem Niveaunetzwerk entfernt. Pro Kante genügt dabei konstante Rechenzeit. Die Rechenzeit für die nicht entfernte Kante zählen wir bei der äußeren Schleife mit. Wenn wir nun über alle maximal n Iterationen der Prozedur Forward(v) summieren, so erhalten wir als Gesamtlaufzeit O(n · n + Anzahl aller entfernten Kanten). Damit ist die Gesamtlaufzeit O(n2 + |E|) = O(n2 ). Satz 9.5.3: Der Algorithmus Forward-Backward-Propagation berechnet einen Sperrfluss in einem Niveaunetzwerk in Laufzeit O(n2 ). Durch iterierte Sperrflussberechnung kann man somit einen maximalen Fluss in Laufzeit O(n3 ) berechnen. 100 10 10.1 Maximale Matchings Problemstellung und eine Charakterisierung optimaler Lösungen Wir beginnen mit einer formalen Definition des betrachteten Problems, von dem wir bereits in Kapitel 9 einen Spezialfall behandelt haben. Definition 10.1.1: Ein Matching in einem ungerichteten Graphen G = (V, E) ist eine Kantenmenge M ⊆ E, so dass je zwei Kanten aus M keinen Knoten gemeinsam haben. Ein Matching M ist für G maximal, wenn es für G kein Matching M 0 mit mehr Kanten als in M gibt. Ein Graph könnte mit den Knoten Personen symbolisieren, wobei Kanten zwischen zwei Personen symbolisieren, dass die beiden ein kooperationsfähiges Team bilden können. Das Problem besteht also in der Zusammenstellung möglichst vieler kooperationsfähiger Teams. Wir erhalten einen Spezialfall des Problems, wenn wir bipartite Graphen betrachten. Dann besteht die Knotenmenge aus zwei disjunkten Mengen U und V und jede Kante verläuft zwischen einem U -Knoten und einem V -Knoten. Dies ist dann der Fall, wenn ein Team zwei Fähigkeiten (Kraft und Geschicklichkeit) braucht und jede Person nur eine der beiden Fähigkeiten besitzt. Das übliche Beispiel lautet, dass ein gütiger Tyrann“ eine ” möglichst große Massenheirat organisieren will, bei der sich alle Paare mögen. Dann wird der Graph bipartit, wenn wir nur von heterosexuellen Beziehungen ausgehen. Wenn wir das Problem auf Teams der Größe k ≥ 3 verallgemeinern, wird es NP-äquivalent. Wir werden auch das Problem der Berechnung maximaler Matchings mit lokaler Suche lösen. Der folgende Lokalitätsbegriff wird sich dabei als geeignet erweisen. Definition 10.1.2: Es sei M ein Matching auf G = (V, E). Die Kanten e ∈ M heißen M -Kanten, alle Kanten e ∈ E − M heißen freie Kanten. Knoten, die auf M -Kanten liegen, heißen besetzt, alle anderen Knoten heißen frei. Ein Pfad p = [u1 , . . . , uk ] heißt M -alternierend, wenn die Kanten {u2i−1 , u2i } frei und die Kanten {u2i , u2i+1 } M -Kanten sind. Der Pfad p heißt M -verbessernd, wenn zusätzlich die Knoten u1 und uk frei und alle Knoten u1 , . . . , uk verschieden sind. Mit Hilfe eines M -verbessernden Pfades erhalten wir sehr leicht ein Matching M 0 , das eine Kante mehr als M enthält. Erstaunlicherweise sind Matchings M ohne M -verbessernde Pfade maximal. Dies zeigen wir in folgendem Satz. Satz 10.1.3: M ist genau dann maximal, wenn es keinen M -verbessernden Pfad gibt. Beweis: Wenn p = [u1 , . . . , uk ] M -verbessernd ist, muss k gerade sein (uk freier Knoten). Sei k = 2j. Wir erhalten ein besseres Matching M 0 , indem wir die j − 1 M -Kanten {u2 , u3 }, . . . , {u2j−2 , u2j−1 } durch die j Kanten {u1 , u2 }, {u3 , u4 }, . . . , {u2j−1 , u2j } ersetzen. Dies ist möglich, da u1 und u2j frei und die Knoten u1 , . . . , u2j verschieden sind. 101 Wir nehmen nun an, dass es ein Matching M 0 mit |M 0 | > |M | gibt. Wir betrachten die Kantenmenge M ⊕ M 0 := {e|e ∈ M und e 6∈ M 0 } ∪ {e|e 6∈ M und e ∈ M 0 }. Da jeder Knoten in jedem Matching auf höchstens einer Kante liegt, ist der Grad jedes Knotens in dem Graphen mit Kantenmenge M ⊕ M 0 höchstens 2. Ungerichtete Graphen mit GradBeschränkung 2 zerfallen in Kreise und Pfade. Da M und M 0 Matchings sind, können sowohl in Kreisen als auch auf Pfaden keine zwei M -Kanten oder zwei M 0 -Kanten aufeinander stoßen. Also haben alle Kreise gerade Länge und gleich viele M -Kanten wie M 0 -Kanten. Da |M 0 | > |M | ist, muss es einen Pfad mit mehr M 0 - als M -Kanten geben. Wieder wegen der Matching-Eigenschaften von M und M 0 , bilden die Kanten eine Folge M 0 , M, M 0 , . . . , M, M 0 . Anfangs- und Endknoten dieses Pfades müssen M -frei sein, sonst wäre die zugehörige M -Kante in M ⊕ M 0 und der Pfad wäre länger. Damit haben wir einen M -verbessernden Pfad erhalten. 2 Wir können nun mit dem leeren Matching (M = ∅) starten. Wenn wir in der Lage sind, effizient zu entscheiden, ob es M -verbessernde Pfade gibt, und gegebenenfalls effizient einen M -verbessernden Pfad zu konstruieren, können wir unseren Ansatz lokaler Verbesserungen durchführen. Da Matchings auf Graphen mit n Knoten nicht mehr als bn/2c Kanten enthalten können, sind wir nach maximal bn/2c lokalen Verbesserungen bei einer optimalen Lösung angelangt. Natürlich ist es aus praktischer Sicht günstiger, mit einem effizienten greedy Ansatz sehr schnell ein möglichst großes Matching M als Startwert zu berechnen, als mit der trivialen Lösung M = ∅ zu beginnen. Es verbleibt die Aufgabe, einen effizienten Algorithmus zur Berechnung M -verbessernder Pfade zu entwerfen. Wir behandeln zunächst den einfacheren Spezialfall bipartiter Graphen. 10.2 Ein Algorithmus für bipartite Graphen Wir betrachten bipartite Graphen mit Kanten zwischen V = {v1 , . . . , vs } und U = {u1 , . . . , ut }. Dann ist n = s + t. Verbessernde Pfade haben gerade viele Knoten, also ungerade Länge. Daher verbinden sie einen V -Knoten mit einem U -Knoten. O.B.d.A. brauchen wir nur von den V -Knoten aus zu suchen. Von allen freien V -Knoten aus suchen wir mit dem BFS-Ansatz. Sei vi ein freier V Knoten. Kandidaten für den ersten Nachfolger auf einem M -verbessernden Pfad sind alle uj ∈ Adj(vi ). Die Kante von uj aus muss dann eine M -Kante sein. Da M ein Matching ist, gibt es höchstens eine M -Kante, die uj berührt. Wenn es keine solche Kante gibt, ist uj frei und wir haben einen verbessernden Pfad gefunden. Wenn {vk , uj } M -Kante ist, müssen wir von vk aus weiter suchen. Falls vk bereits auf dem Anfangsstück eines verbessernden Pfades liegt, muss von vk aus nicht ein weiteres Mal gesucht werden. Zielknoten sind alle freien u-Knoten. Zunächst betrachten wir das in Abbildung 10.2.1 dargestellte Beispiel. Das bisher errechnete Matching enthält 5 Kanten, einziger freier v-Knoten und somit Startknoten ist v2 , einziger Zielknoten u1 . Wir suchen mit dem BFS-Ansatz. Von v2 aus gehen 2 freie Kanten aus: v2 − u2 , dann muss auf einem alternierenden Pfad die Kante u2 − v3 folgen, und 102 v1 u1 v2 u2 v3 u3 v4 u4 v5 u5 v6 u6 freie Kanten M -Kanten Abbildung 10.2.1: Ein bipartiter Graph mit einem Matching. v2 − u6 − v5 . Von v3 gibt es nur die Fortsetzung v3 − u3 − v4 . Von v5 gibt es im Prinzip drei Fortsetzungen, die über u3 muss nicht betrachtet werden, da u3 bereits über v3 erreicht wurde, also betrachten wir nur v5 − u4 − v1 und v5 − u5 − v6 . Von v4 aus können nur die bereits bekannten“ Knoten u5 und u6 erreicht werden. Von v1 aus ist der freie Knoten ” u1 erreichbar. Wir haben den verbessernden Pfad v2 − u6 − v5 − u4 − v1 − u1 gefunden (siehe Abbildung 10.2.2). u2 v2 v3 u3 v4 v5 u4 v1 u5 v6 u6 u1 frei Abbildung 10.2.2: Die BFS-Suche nach M -verbessernden Pfaden. Das neue Matching enthält also die neuen Kanten v2 − u6 , v5 − u4 und v1 − u1 , sowie die alten Kanten v3 − u2 , v4 − u3 und v6 − u5 . Da V sechs Knoten enthält, ist dieses Matching maximal. Wir stellen fest, dass bei unserer Vorgehensweise die u-Knoten eine unwichtige Rolle spielen, von ihnen aus müssen die M -Kanten benutzt werden. Wir stellen die Information übersichtlicher dar, indem wir auf den v-Knoten einen gerichteten Hilfsgraphen H aufbauen. Die Kante (vi , vj ) soll genau dann existieren, wenn es ein uk gibt, so dass {vi , uk } freie Kante und {uk , vj } M -Kante ist. Außerdem soll label(vi ) definiert werden: label(vi ) = 0, falls vi nicht mit einem freien u-Knoten verbunden ist, ansonsten soll label(vi ) ein beliebiger freier u-Knoten sein, mit dem vi verbunden ist. Dieser Hilfsgraph kann in Zeit O(n + m) mit einem einmaligen Durchlauf der Adjazenzlisten der v-Knoten erstellt werden. In unserem Beispiel ergibt sich der in Abbildung 10.2.3 beschriebene Graph. Durch Betrachtung des Matchings können in Zeit O(m) die freien v-Knoten als Startknoten der BFS-Suche festgestellt werden, wobei m die Anzahl der Kanten ist. Zielknoten 103 v1 v2 v3 v4 v5 v6 Abbildung 10.2.3: Der Hilfsgraph. sind alle v-Knoten, die ein label(v) 6= 0 tragen. In unserem Fall ist v2 Startknoten und v1 Zielknoten. In H wird mit dem BFS-Ansatz nach einem Zielknoten gesucht, es wird der Weg v2 − v5 − v1 gefunden. Der verbessernde Pfad kann zurückverfolgt werden, wenn für jeden erreichten v-Knoten pred(v) den Vorgänger angibt. Insgesamt kann in Zeit O(n+m) die Suche nach einem M -verbessernden Pfad durchgeführt werden. Da Matchings nicht mehr als min {s, t} Kanten haben können, haben wir folgenden Satz gezeigt. Satz 10.2.1: Mit dem BFS-Ansatz können maximale Matchings in bipartiten Graphen in Zeit O(m · min (s, t)) berechnet werden. Algorithmus 10.2.2: Wir beschreiben den vorgestellten Algorithmus noch etwas formaler. Das Matching wird durch ein Array dargestellt, m(v) bzw. m(u) ist der MatchingNachbar des Knotens, es ist m(·) = 0, falls der Knoten nicht im Matching enthalten ist. A soll die Kantenmenge des Hilfsgraphen H enthalten, label(v) ist ein freier u-Nachbar von v oder, falls nicht vorhanden, 0. Für die BFS-Suche wird die Queue Q benutzt. Für jeden Knoten v ist pred(v) der Vorgänger, von dem aus v erreicht wurde, pred(v) = 0 für Startknoten im BFS-Ansatz. 0.) m(u) := 0 für u ∈ U ; m(v) := 0 für v ∈ V . 1.) A := ∅; label(v) := 0 für v ∈ V ; für {v, u} ∈ E: if m(u) = 0 then label(v) := u else if m(u) 6= v then A := A ∪ {(v, m(u))}. 2.) Q := empty queue; für v ∈ V : if m(v) = 0 then v → Q; pred(v) := 0. 3.) while Q 6= ∅: v ← Q; if label(v) 6= 0 then verbessere(v); gehe zu Schritt 1 else für v 0 ∈ V mit pred(v 0 ) = 0 und (v, v 0 ) ∈ A : pred(v 0 ) := v; v 0 → Q. 4.) Der Algorithmus stoppt, wenn die while-Schleife abbricht, ohne dass Schritt 1 aufgerufen wurde. 104 verbessere(v) if pred(v) = 0 then m(v) := label(v); m(label(v)) := v else label(pred(v)) := m(v); m(v) := label(v); m(label(v)) := v; verbessere(pred(v)). Es bleibt der Leserin und dem Leser überlassen, die Richtigkeit und Effizienz dieser Implementierung zu überprüfen. 10.3 Blüten Der Algorithmus aus Kapitel 10.2 basiert auf der graphentheoretischen Charakterisierung maximaler Matchings aus Kapitel 10.1. Diese Charakterisierung gilt für beliebige Graphen. Finden wir mit dem BFS-Ansatz stets verbessernde Pfade? Dazu betrachten wir den in Abbildung 10.3.1 dargestellten Graphen. v14 v13 v12 v11 v10 v2 v1 v3 v9 v8 v4 v5 v6 v7 Abbildung 10.3.1: Das Beispiel eines nicht bipartiten Graphen. Freie Knoten sind v1 und v14 . Wir führen eine BFS-Suche durch. Von v1 werden im Hilfsgraphen H v12 und v3 , von v14 wird v12 gefunden. Wenn v12 schon als bereits besucht abgehakt wurde, können wir nie mehr den Zielknoten v13 , der mit dem freien Knoten v14 verbunden ist, finden. Also müssen hier Wege von allen freien Knoten aus einzeln betrachtet werden. Von v1 aus finden wir den verbessernden Pfad v1 , v3 , v5 , v7 , v9 , v11 , v13 und den alternierenden Pfad v1 , v12 , v10 , v8 , v6 , v4 , v2 , wobei v2 mit dem freien Knoten v1 verbunden ist. Allerdings erhalten wir keinen M -verbessernden Pfad, da Anfangs- und Endpunkt des Pfades nicht verschieden sind. Zielknoten sind Knoten, die mit freien Knoten, die vom Anfangspunkt verschieden sind, verbunden sind. Von v14 aus werden folgende Wege gefunden, wobei v13 und v2 , die mit v1 verbunden sind, Zielknoten sind: v14 , v12 , v10 , v8 , v6 , v4 , v2 , ein verbessernder Pfad, und v14 , v12 , v10 , v9 , v11 , v13 , ein Problempfad“. Er sieht aus wie ” 105 ein verbessernder Pfad, aber er enthält mehrere Knoten doppelt. Warum treten derartige Probleme bei bipartiten Graphen nicht auf? Knoten werden auf Wegen doppelt erreicht, wenn die Wege Kreise enthalten. Kreise gerader Länge (siehe Abbildung 10.3.2) werden über eine freie Kante erreicht und verlassen. x v w ... u z Abbildung 10.3.2: Ein Kreis gerader Länge. Bei der Suche von v aus kann w und danach z erreicht werden. Wenn der Kreis umrundet wird, erreichen wir x. Danach kann w nicht über u erreicht werden sondern nur z. Der BFS-Ansatz erkennt, dass z bereits besucht wurde, und sucht auf anderen Wegen weiter. Kreise ungerader Länge haben eine freie Kante mehr als M -Kanten. An einer Stelle treffen zwei freie Kanten aufeinander. Wenn der Kreis an anderer Stelle erreicht wird, kann er nicht umrundet werden. Ansonsten ergibt sich eine Situation wie in Abbildung 10.3.3. u2n v w u2n−1 u2j+1 u2j u2j−2 u2j−1 ... u0 u1 u2 Abbildung 10.3.3: Ein Kreis ungerader Länge. Bei der BFS-Suche wird u0 erreicht. Dann wird in beiden Richtungen auf dem Kreis weitergesucht, auf der gleichen Stufe werden u2j−2 und u2j+1 erreicht. Im nächsten Schritt sollen beide alternierenden Pfade über die Kante {u2j−1 , u2j } fortgesetzt werden. Schließlich erreichen beide Wege wieder w. Diese Problemsituation tritt in bipartiten Graphen nicht auf, da bipartite Graphen keine Kreise ungerader Länge enthalten. Nur Kreise ungerader Länge der oben beschriebenen Art führen zu gefälschten“ alternierenden Pfaden. Derartige Kreise werden daher Blüten ” genannt. Definition 10.3.1: Es sei G ein ungerichteter Graph und M ein Matching in G. Eine M -Blüte b mit Basis u0 ist ein Kreis ungerader Länge 2k + 1 mit k + 1 freien Kanten und k M -Kanten, wobei sich in u0 zwei freie Kanten treffen und sich ansonsten freie und M -Kanten abwechseln. Wie können wir bei der BFS-Suche Blüten erkennen? Wir starten stets nur von einem freien Knoten aus. Jeder Knoten erhält ein seen-Bit, das zunächst auf 0 gesetzt wird. 106 In unserem Hilfsgraphen gehen wir immer einen Doppelschritt v − u − w, wobei {v, u} eine freie und {u, w} eine M -Kante ist. Der Knoten w erhält v als Vorgänger, für u wird nun seen(u) = 1 gesetzt. Genau dann, wenn seen(u) = 1 und seen(m(u)) = 1 ist, haben wir die M -Kante {u, m(u)} in beiden Richtungen benutzt. Dies ist äquivalent dazu, eine M -Blüte gefunden zu haben. Blüten können also effizient entdeckt werden. Wie sollen Blüten behandelt werden? Wir werden zeigen, dass wir Blüten zu einem Knoten schrumpfen lassen können. In dem neuen Graphen Gb identifizieren wir ein Matching Mb , so dass es in G genau dann einen M -verbessernden Pfad gibt, wenn es in Gb einen Mb -verbessernden Pfad gibt. Zudem gibt es einen effizienten Algorithmus, um aus einem Mb -verbessernden Pfad in Gb einen M -verbessernden Pfad in G zu konstruieren. Definition 10.3.2: Sei b eine M -Blüte in G = (V, E). Es sei Gb = (Vb , Eb ) der folgende Graph. Vb enthält alle Knoten aus V , die nicht zu b gehören, und den neuen Knoten vb . Eb enthält alle Kanten aus E, die zwei Knoten außerhalb von b verbinden. Dazu enthält Eb die Kante {v, vb }, wenn es in G eine Kante von v zu einem Knoten in der Blüte gibt. Mb enthält alle Kanten aus M , die zwei Knoten außerhalb von b verbinden, und, falls M die Kante {w, u0 } zur Basis von b enthält, die Kante {w, vb }. Es ist offensichtlich, dass Mb ein Matching auf Gb ist. In G ist {w, u0 } die einzige Matchingkante, die genau einen Knoten auf der Blüte hat. Wir wollen unser angekündigtes Vorgehen durchführen. Lemma 10.3.3: Wenn wir bei der Suche vom freien Knoten u den Knoten x auf einer Blüte b erreichen und diese Blüte bei der Suche nach verbessernden Pfaden von u aus entdeckt wird, dann gibt es einen alternierenden Pfad von u nach x, der mit einer M Kante endet. Beweis: Da die Blüte entdeckt wird, gibt es einen alternierenden Pfad von u zur Blütenbasis u0 , der mit einer M -Kante endet. Danach ist eine der beiden Kreisrichtungen geeignet. 2 Lemma 10.3.4: Sei b eine M -Blüte in G, die bei der Suche nach einem M -verbessernden Pfad von u aus gefunden wird. Sei u0 die Basis von b. Es gibt in G genau dann einen M -verbessernden Pfad von u aus, wenn es in Gb einen Mb -verbessernden Pfad von u (falls u = u0 , von vb ) aus gibt. Beweis: Wir nehmen zunächst an, dass p ein Mb -verbessernder Pfad von u (bzw. vb ) in Gb ist. Falls p nicht durch vb geht, ist p auch M -verbessernd in G. Ansonsten lässt sich p folgendermaßen zerlegen: u ; w − vb − w0 ; u0 . Da die Richtung auf diesem Pfad p0 p00 ∧ nicht entscheidend ist, sei o.B.d.A. {w, vb } M -Kante (oder nicht vorhanden, d.h. u = u0 = vb ) und {vb , w0 } freie Kante. Falls w existiert, ist {w, u0 } M -Kante in G. Außerdem gibt es ein uj auf der Blüte, so dass {uj , w0 } M -freie Kante in G ist. Wir benutzen nun in G den nach Lemma 10.3.3 existierenden alternierenden Pfad von u nach uj , der mit einer 107 M -Kante endet, die freie Kante {uj , w0 } und p00 . Wir erhalten in G einen M -verbessernden Weg von u nach u0 . Wir bemerken bereits hier, dass sich dieser Weg effizient konstruieren lässt, wenn die Blüte b als Kreis abgespeichert ist. Wir nehmen nun an, dass p ein M -verbessernder Pfad in G von u aus ist. Wir wollen die Existenz eines Mb -verbessernden Pfades pb in Gb von u bzw. vb aus beweisen. Der Beweis dieser Aussage ist entscheidend dafür, dass wir sicher sein können, alternierende Pfade nicht zu übersehen. Für den Entwurf des Algorithmus hat dieser Teil des Beweises keine Konsequenzen. Die folgende Fallunterscheidung ist zwar ärgerlich lang, aber notwendig. 1. Fall: p berührt b nicht. Dann ist pb = p geeignet. 2. Fall: p enthält die M -Kante {w, u0 } für die Basis u0 von b. Wenn u0 der einzige bKnoten auf p ist, kann u0 einfach durch vb ersetzt werden. Ansonsten sei uj der andere Endknoten des Pfades auf der Blüte, dann ist die Kante {uj , w0 }, die die Blüte verlässt, eine freie Kante. Der ganze Pfadabschnitt zwischen u0 und uj (selbst wenn er Teile außerhalb der Blüte enthält) wird durch vb ersetzt. Der neue Pfad pb ist alternierend, da {w, vb } Mb -Kante und {vb , w0 } freie Kante ist. 3. Fall: Ansonsten lässt sich p folgendermaßen zerlegen: p : u ; u i ; uj ; z p1 p2 p3 Dabei ist p1 ein Pfad außerhalb der Blüte, dessen letzte Kante frei ist, die letzte Kante auf p2 ist eine M -Kante auf b, die erste Kante auf p3 ist frei, und p3 verläuft außerhalb von b. Da b gefunden wird, gibt es einen alternierenden Pfad q von u nach u0 , der mit einer M -Kante endet. Die folgende Konstruktion benutzt eine Cut-and-Paste-Technik auf den Pfaden p und q in G. Mit p1 , p3 und q bezeichnen wir auch die entsprechenden Pfade in Gb , d. h. p1 : u ; vb , p3 : vb ; z, q : u ; vb . Zu einem Pfad t und einem Knoten y ∈ t bezeichne t(y) das Anfangssegment von t bis zum Knoten y, t0 (y) das Endsegment von t ab dem Knoten y und tR den Pfad t in umgekehrter Orientierung. Für zwei Knoten y1 , y2 ∈ t besage die Relation y1 < y2 , dass y1 auf t vor y2 liegt. Wir konstruieren den verbessernden Pfad pb mit Hilfe einer induktiv definierten Knotenfolge v1 < x1 < v2 < x2 < · · · ∈ q, wobei die vi auf p1 und die xi auf p3 liegen. Der Pfad p1 (vi ) wird stets ein Anfangsstück des zu konstruierenden Pfades pb sein. Die induktive Konstruktion wird durch die Abbildung 10.3.4 veranschaulicht: (1) v1 = u. Allgemein sei vi definiert. 108 u = v1 q p3 vi xi p3 q p1 q vi+1 q xi+1 p1 q vb Abbildung 10.3.4: Eine Hilfskonstruktion. (2) Es sei xi der letzte Knoten auf p3 , der auch auf q 0 (vi ) liegt (Existenz eines solchen xi ist wegen vb ∈ p3 ∩ q 0 (vi ) gesichert). Falls nun der Pfad u ; xi ; z q p3 alterniert (Abbruchbedingung 1), dann definieren wir pb als pb : u ; v i ; xi ; z p1 q p3 und brechen die induktive Konstruktion ab. Abbruchbedingung 1 ist insbesondere erfüllt, wenn xi = vb ist. Wir setzen daher für die Fortsetzung der induktiven Konstruktion xi 6= vb voraus. (3) Es sei vi+1 der früheste Knoten von p1 , der auch zu q 0 (xi ) gehört (Existenz eines solchen vi+1 ist wegen vb ∈ p1 ∩ q 0 (xi ) garantiert). Falls nun der Pfad u ; vi+1 ; xi R p1 q alterniert (Abbruchbedingung 2), dann definieren wir pb als pb : u ; vi+1 ; xi ; z R p1 q p3 und brechen die induktive Konstruktion ab. Abbruchbedingung 2 ist insbesondere erfüllt, wenn vi+1 = vb ist. Wir setzen daher für die Fortsetzung der induktiven Konstruktion vi+1 6= vb voraus. Nach endlich vielen Schritten wird vb erreicht. Daher ist irgendwann eine der zwei Abbruchbedingungen erfüllt. Es bleibt zu zeigen, dass in beiden Fällen pb ein verbessernder Pfad in Gb ist. 109 Fall 1: Die induktive Konstruktion werde durch Abbruchbedingung 1 gestoppt. Die Knotenfolgen vi , xi sind so definiert, dass p1 (vi ) und q 0 (vi ) keine Knoten außer vi gemeinsam haben (im Falle i > 0 gilt sogar, dass p1 (vi ) und q 0 (xi−1 ) keine Knoten außer vi gemeinsam haben). Analog haben q 0 (vi ) und p03 (xi ) keine Knoten außer xi gemeinsam. Daher sind die zur Konstruktion von pb benutzten Pfadstücke von p1 , q und p3 (bis auf die Nahtstellen) knotendisjunkt. Also ist pb ein Pfad ohne Knotenwiederholung. Wegen Abbruchbedingung 1 alterniert pb an der Stelle xi . Da entweder vi = u gilt oder Abbruchbedingung 2 verletzt ist, ist das Alternieren von pb auch an der Stelle vi gewährleistet. Also ist pb ein verbessernder Pfad. Fall 2: Die induktive Konstruktion werde durch Abbruchbedingung 2 gestoppt. Die Pfade p1 (vi+1 ) und q 0 (xi ) haben nur den Knoten vi+1 gemeinsam. Die Pfade q 0 (xi ) (sogar q 0 (vi )) und p03 (xi ) haben keine Knoten außer xi gemeinsam. Daher sind die zur Konstruktion von pb benutzten Pfadstücke von p1 , q und p3 bis auf die Nahtstellen knotendisjunkt. Also ist pb ein Pfad ohne Knotenwiederholung. Wegen Abbruchbedingung 2 alterniert pb an der Stelle vi+1 . Da Abbruchbedingung 1 verletzt ist, ist das Alternieren von pb auch an der Stelle xi gewährleistet. Also ist pb ein verbessernder Pfad. Damit wurde in allen Fällen gezeigt, dass ein verbessernder Pfad p in G in einen verbessernden Pfad pb in Gb transformierbar ist. 2 10.4 Ein Algorithmus für maximale Matchings in allgemeinen Graphen Wir suchen nun nacheinander von allen freien Knoten aus nach verbessernden Pfaden und müssen dabei Blüten bemerken und beachten. Wir zeigen, dass erfolglose Suchen nicht wiederholt werden müssen. Diese Erkenntnis vermeidet einen Θ(n)-Faktor in der Rechenzeit. Satz 10.4.1: Falls es in G keinen M -verbessernden Pfad von u aus gibt und M 0 ein Matching ist, das aus M mittels eines M -verbessernden Pfades berechnet wurde, gibt es in G keinen M 0 -verbessernden Pfad von u aus. Beweis: Sei p der M -verbessernde Pfad, mit dessen Hilfe M 0 entsteht. Wir nehmen nun an, dass q in G ein M 0 -verbessernder Pfad ist, der von u aus startet. 1. Fall: q und p haben keinen Knoten gemeinsam. Dann ist q auch M -verbessernd. Widerspruch! 2. Fall: Sei v der erste Knoten von q, der auf p liegt. Wir zerlegen p und q am Knoten v: p: x ; v ; y p1 p2 q : u ; v ; z. q1 110 q2 Einer der beiden folgenden Pfade von u aus ist alternierend und damit M -verbessernd. Pfad 1: u ; v ; x R q1 p1 Pfad 2: u ; v ; y. q1 p2 Dabei ist zu beachten, dass x und y freie Knoten sind. 2 Wir wollen nun einen Algorithmus zur Berechnung maximaler Matchings skizzieren. Es kann mit dem leeren Matching begonnen werden. Es wird stets von freien Knoten aus nach verbessernden Pfaden gesucht. Ist die Suche von u aus erfolglos, muss nach Satz 10.4.1 nie wieder von u aus gesucht werden. Da ein Knoten, der in ein Matching aufgenommen wird, durch verbessernde Pfade das Matching nicht mehr verlässt, wird nur n-mal eine Suche gestartet. Die Suche verläuft auf der Grundlage des Verfahrens in Kapitel 10.2. Es werden Zielknoten mit einem label angezeigt. Um Blüten zu erkennen, wird ein seenarray benutzt (siehe Kapitel 10.3). Eine erkannte Blüte kann über pred“ und m“ leicht ” ” konstruiert und dann zu einem Knoten geschrumpft werden. Wir erinnern uns, dass m(v) den Matchingpartner von v bezeichnet und pred(v) den (Vor-)Vorgänger von v auf dem verbessernden Pfad. Der für die Blüte neu erzeugte Knoten erhält die nächste fortlaufende Nummer, die i-te Blüte erhält also die Nummer n + i. Für die Blüte werden die in ihr vorkommenden Knoten, die wiederum Blütenknoten sein können, abgespeichert. Wenn ein Knoten v auf der Blüte Zielknoten ist, da er mit dem freien Knoten u verbunden ist, wird der Blütenknoten vb Zielknoten, auch er ist mit dem freien Knoten u verbunden. Wie bereits gesehen, wird von jedem Knoten u aus nur einmal gesucht. Die BFS-Routine verursacht, ohne die Behandlung der Blüten, Zeit O(n + m). Die Behandlung jeder Blüte ist in Zeit O(n+m) möglich, wie sich direkt aus der obigen Beschreibung ergibt. Da Blüten mindestens 3 Knoten haben und auf einen Knoten schrumpfen, können bei der Suche von u aus höchstens n/2 Blüten gefunden werden. Nach Konstruktion hat Gb weniger Kanten als G. Also lässt sich die Suche von u aus in Zeit O(n(n + m)) inklusive der Behandlung der Blüten durchführen. Satz 10.4.2: Maximale Matchings können in O(n2 m) = O(n4 ) Rechenschritten berechnet werden. Mit dem Einsatz besserer Datenstrukturen lässt sich die Rechenzeit auf O(nm) = O(n3 ) senken. Es sind sogar effizientere (aber kompliziertere) Algorithmen bekannt, die mit einer Rechenzeit von O(n1/2 m) = O(n5/2 ) auskommen. Wir beenden das Kapitel mit einem nicht ganz kleinen Beispiel. Beispiel 10.4.3: Wir betrachten den Graphen in Abbildung 10.4.1. Das vorhandene Matching mit den Kanten {1, 2}, {3, 4}, {5, 6}, {7, 8}, {9, 10}, {11, 12}, {13, 14} kann um höchstens eine Kante verbessert werden. Freie Knoten sind 15 (Zielknoten 2 und 4), 16 (Zielknoten 5, 7 und 14) und 17 (Zielknoten 10 und 12). 111 15 5 16 2 1 4 3 6 8 7 11 14 13 9 10 17 12 Abbildung 10.4.1: Der gegebene Graph. Bei der Suche von 15 aus sind 5, 7, 10, 12 und 14 Zielknoten. Von 15 aus wird der Hilfsgraph aufgebaut. Nachfolger sind 1 und 3, deren einzige Nachfolger sind 3 bzw. 1. Also kann von 15 kein Zielknoten gefunden werden. Der Knoten 15 wird im Folgenden nicht mehr betrachtet. Bei der Suche von 16 aus sind nur noch 10 und 12 Zielknoten. Nachfolger von 16 im Hilfsgraphen werden 6, 8 und 13, die Knoten 5, 7 und 14 erhalten den seen-Wert 1. Von 6 aus ergeben sich im Hilfsgraphen die Nachfolger 3, 7 und 8. Da seen(7) = 1, haben wir eine Blüte gefunden. Die freie Kante {6, 8} ist die mittlere Kante, es schließen sich die M Kanten {5, 6} und {7, 8} an. Die Knoten 6 und 8 wurden beide von 16 im Hilfsgraphen erreicht. Also schrumpft die Blüte 16, 5, 6, 8, 7, 16 auf den Blütenknoten 18. Der neue Graph ist in Abbildung 10.4.2 dargestellt. Von 18 aus können nun 10 und 12 erreicht werden, beides Zielknoten. (18, 9, 10, 17) ist verbessernder Pfad. Wir erhalten hierbei 9 als m(10) und 17 als label(10). Wir erkennen, dass die Kante {18, 9} ein Ersatz für die Kante {8, 9} war. Es ist (16, 7, 8) der Pfad in der Blüte zu 8, der mit einer M -Kante endet. Also ist (16, 7, 8, 9, 10, 17) der verbessernde Pfad in G. Die M -Kanten {7, 8}, {9, 10} werden durch {16, 7}, {8, 9} und {10, 17} ersetzt. Das neue Matching ist maximal. 112 2 1 4 3 9 18 11 14 10 13 17 12 Abbildung 10.4.2: Der geschrumpfte Graph. 3 6 7 16 3 8 18 13 13 Abbildung 10.4.3: Die Änderung des Hilfsgraphen. Es ist seen(4) = seen(14) = 1. 113 11 11.1 Algorithmen für Probleme der elementaren Zahlentheorie Kryptographische Systeme Die Zahlentheorie zählt zu den ältesten mathematischen Disziplinen. Allerdings befasst sich die mathematische Zahlentheorie vor allem mit strukturellen Aspekten. Die Entwicklung effizienter Algorithmen für Probleme der Zahlentheorie gelangte erst mit der Entwicklung der Informatik in den Mittelpunkt des Interesses. Besonders die Kryptographie, die sich mit Chiffriermethoden befasst, stellt ein Bindeglied zwischen der Zahlentheorie und dem Entwurf effizienter Algorithmen dar. Der Vorteil effizienter Algorithmen in der Zahlentheorie, ob bei der Berechnung des größten gemeinsamen Teilers zweier Zahlen oder bei dem Test, ob eine gegebene Zahl prim ist, zeigt sich vor allem bei großen Zahlen. Zur Motivation der in diesem Kapitel behandelten Probleme stellen wir daher zunächst das RSA-System vor. Dieses System ist Grundlage der am häufigsten benutzten, mit öffentlich bekannten Schlüsseln arbeitenden kryptographischen Verfahren. Es benötigt effiziente Algorithmen für den Umgang mit sehr langen Binärzahlen und die meisten grundlegenden Probleme der elementaren Zahlentheorie. In Kapitel 11.2 stellen wir effiziente Algorithmen für die Berechnung von an , des größten gemeinsamen Teilers zweier Zahlen und der multiplikativen Inversen in m vor. Es folgt in Kapitel 11.3 mit dem chinesischen Restklassensatz ein zentrales Hilfsmittel für das Rechnen mit großen Zahlen. In Kapitel 11.4 führen wir die benötigten Ergebnisse aus der Zahlentheorie ein. Damit können wir in Kapitel 11.5 die effiziente Berechnung des Jacobi-Symbols behandeln. Schließlich stellen wir in Kapitel 11.6 mit dem Primzahltest von Solovay und Strassen einen effizienten randomisierten Algorithmus für ein Problem vor, für das es noch keinen befriedigenden deterministischen Algorithmus gibt. Die Kryptographie, wörtlich übersetzt die Lehre von den Geheimschriften, ist eine sehr alte Wissenschaft, die bis in die 70er Jahre hinein fast ausschließlich von militärischen Anforderungen geprägt wurde. Schon Cäsar verschlüsselte Nachrichten, allerdings auf sehr einfache Weise. Eine auf seiner Verschlüsselung beruhende Familie von Chiffrierfunktionen wird Cäsar-Chiffre genannt. Heutzutage kann sich jeder kryptographischer Verfahren bedienen. Um den unberechtigten Zugriff auf schutzwürdige Daten zu verhindern, werden Daten verschlüsselt. Da über einen langen Zeitraum das gleiche kryptographische System verwendet werden soll, wird davon ausgegangen, dass das benutzte kryptographische Verfahren allgemein bekannt ist. Definition 11.1.1: Ein kryptographisches System besteht aus folgenden Komponenten: – der endlichen Schlüsselmenge K (z.B. {0, 1}300 ), – der Nachrichtenmenge M (z.B. {0, 1}∗ , der Menge der endlichen Folgen über {0, 1}), – der Menge der Kryptogramme Y (z.B. {0, 1}∗ ), 114 – einer Chiffrierfunktion E : K × M → Y und – einer Dechiffrierfunktion D : K × Y → M , so dass D(k, E(k, m)) = m für alle m ∈ M und k ∈ K ist. Wie wird ein solches kryptographisches System benutzt? Wenn zwei Benutzer kommunizieren wollen, vereinbaren sie einen Schlüssel k ∈ K. Der Absender einer Nachricht m verschlüsselt diese mit Hilfe des Schlüssels, indem er das Kryptogramm y = E(k, m) berechnet. Das Kryptogramm wird über einen unter Umständen unsicheren Kommunikationskanal an den Empfänger gesendet. Der Empfänger kann das Kryptogramm entschlüsseln, indem er m = D(k, y) berechnet. Damit die Verwendung des kryptographischen Systems die Kommunikation nicht wesentlich verlangsamt, sollten die notwendigen Operationen, also die Vereinbarung eines Schlüssels und die Anwendung der Chiffrierund Dechiffrierfunktion, effizient durchführbar sein. Darüber hinaus soll die Nachricht bei der Kommunikation geschützt sein. Ein Dritter, hier Gegner genannt, der das Kryptogramm y abfängt, soll daraus (fast) keinen Informationsgewinn erlangen können. Daher muss ihm auf jeden Fall der Schlüssel k unbekannt bleiben, denn sonst könnte er das Kryptogramm wie der legale Benutzer dechiffrieren. Wegen der Redundanz jeder natürlichen Sprache, die sich in den Nachrichten widerspiegelt, sind alle kryptographischen Systeme, die mit endlich vielen verschiedenen Schlüsseln arbeiten, im Prinzip zu knacken“. Der Gegner kann, wenn das Kryptogramm lang genug ” ist, die Nachricht m und den verwendeten Schlüssel k mit hoher Wahrscheinlichkeit korrekt errechnen. Die Sicherheit kryptographischer Systeme beruht darauf, dass der Gegner zur Berechnung von m und/oder k so viel Aufwand treiben muss, dass sich die Berechnung für ihn nicht lohnt oder dass die benötigten Ressourcen praktisch nicht verfügbar sind, da er z.B. 10000 Jahre CPU-Zeit auf dem schnellsten Rechner benötigt. Ein sicheres kryptographisches System muss also so beschaffen sein, dass die von den legalen Benutzern auszuführenden Operationen effizient durchführbar sind, während es für die Kryptoanalyse durch einen Gegner keinen effizienten Algorithmus gibt. Bis zum Jahr 1976 waren nur kryptographische Systeme mit geheimen Schlüsseln (secret key cryptosystems) bekannt. Dabei tauschen die Kommunikationspartner den von ihnen verwendeten Schlüssel auf einem sicheren Kommunikationsweg aus, etwa mit Hilfe eines vertrauenswürdigen Kuriers. Im militärischen Bereich sind derartige Systeme gut einsetzbar. Auch zur Datensicherung, an der nur ein Benutzer oder ein Rechner beteiligt ist, eignen sich Systeme mit geheimen Schlüsseln. In postalischen Netzen (Telefon, Internet, . . .) sind solche Systeme aber ungeeignet. Das Wesen dieser Kommunikationssysteme besteht ja gerade darin, dass jeder Benutzer mit jedem anderen, ihm zuvor eventuell unbekannten Benutzer kommunizieren kann. Die Vereinbarung eines Schlüssels auf sicherem Weg ist dann nicht möglich, wenn man die Post nicht für einen vertrauenswürdigen Kurier hält. Diffie und Hellman (1976) haben die Benutzung kryptographischer Systeme mit öffentlich bekannten Schlüsseln (public key cryptosystems) vorgeschlagen, ein auf den ersten Blick undurchführbarer Vorschlag. Wenn alle Informationen öffentlich sind, muss doch der Gegner genauso wie der Empfänger einer Nachricht das Kryptogramm entschlüsseln können. 115 Der faszinierende Trick in dem Vorschlag von Diffie und Hellman lässt sich einfach beschreiben. Jeder Benutzer B erzeugt sich einen Schlüssel kB und eine mit dem Schlüssel verbundene zusätzliche Information I(kB ). Der Schlüssel kB wird öffentlich bekannt gemacht. Wenn Benutzer A eine Nachricht m an B senden will, benutzt er den Schlüssel kB und berechnet das Kryptogramm y = E(kB , m). Die Zusatzinformation I(kB ) muss nun so beschaffen sein, dass die Dechiffrierung, also die Berechnung von m = D(kB , y), mit Hilfe von y, kB und I(kB ) auf effiziente Weise möglich ist, während eine effiziente Berechnung von m aus y und kB , ohne I(kB ) zu kennen, mit den zur Verfügung stehenden Ressourcen unmöglich ist. Rivest, Shamir und Adleman (1978) haben ein vermutlich sicheres kryptographisches System mit öffentlich bekannten Schlüsseln entworfen. Wir wollen dieses so genannte RSASystem vorstellen, bevor wir den Zusatz vermutlich“ im vorigen Satz erläutern. ” Jeder Benutzer B erzeugt sich zwei Primzahlen p, q ≥ 2l mit je l+1 Bits. Heutzutage muss l mindestens 300 sein. Zur Erzeugung von p und q werden Zufallszahlen darauf getestet, ob sie Primzahlen sind. Da es genügend viele Primzahlen gibt, müssen im Durchschnitt nicht zu viele Versuche unternommen werden. Für diesen Schritt stellen wir in Kapitel 11.6 einen effizienten, randomisierten Primzahltest vor, der mit sehr kleiner Wahrscheinlichkeit auch Zahlen, die keine Primzahlen sind, als Primzahlen klassifiziert. Durch einen derartigen Fehler erhält der Gegner keine zusätzliche Information, allerdings kann die Kommunikation zwischen den beiden legalen Benutzern gestört sein. In diesem seltenen Fall muss B einen neuen Schlüssel erzeugen. Im Normalfall berechnet B nun n = pq und den Wert der Euler-Funktion ϕ(n). Hierbei ist ϕ(n) die Anzahl aller a ∈ {1, . . . , n − 1}, die mit n keinen gemeinsamen Teiler haben. Für Primzahlen p und q ist ϕ(pq) = (p − 1)(q − 1). Außerdem erzeugt sich B eine Zufallszahl e ∈ {0, . . . , n − 1}, die teilerfremd zu ϕ(n) ist, und berechnet d ≡ e−1 mod ϕ(n). In Kapitel 11.2 geben wir effiziente Algorithmen für die Berechnung von größten gemeinsamen Teilern und von multiplikativen Inversen in ∗m an. Der öffentlich bekannt gegebene Schlüssel k besteht aus dem Paar (n, e), während das Paar (ϕ(n), d) die geheime Zusatzinformation von B ist. Nehmen wir nun an, dass A eine Nachricht m ∈ {0, 1}∗ an B senden will. Dann zerlegt A die Nachricht m in Blöcke mi der Länge 2l. Jeder Block wird als Binärzahl aufgefasst, deren Wert ebenfalls mit mi bezeichnet wird. Es ist 0 ≤ mi < 22l . Die Chiffrierfunktion E ist definiert durch E((n, e), mi ) ≡ mei mod n und kann (siehe Kapitel 11.2) effizient berechnet werden. Sei D((n, e), y) ≡ y d mod n, dann ist D ebenfalls effizient berechenbar. Es ist D((n, e), E((n, e), mi )) ≡ med i mod n. Da nach Konstruktion ed ≡ 1 mod ϕ(n) ist, folgt aus der elementaren Zahlentheorie med i ≡ 2l mi mod n. Da n ≥ 2 ist, haben wir mit mi mod n auch mi eindeutig berechnet. 116 Für die von uns verwendeten und nicht bewiesenen Aussagen der elementaren Zahlentheorie und für weitere Resultate verweisen wir auf die Bücher An Introduction to the Theory ” of Numbers“ von I. Niven und H.S. Zuckerman, Einführung in die Zahlentheorie“ von ” P. Bundschuh oder Algorithmic Number Theory, Volume I, Efficient Algorithms“ von ” E. Bach und J. Shallit. Die für die folgenden Abschnitte angekündigten Resultate implizieren also, dass die legalen Benutzer das RSA-System effizient benutzen können. Es gibt bisher keinen effizienten Algorithmus für die Kryptoanalyse, also keinen effizienten Algorithmus, mit dem der Gegner das RSA-System effizient knacken kann. Wenn er in der Lage wäre, Zahlen effizient zu faktorisieren, also in ihre Primfaktoren zu zerlegen, dann könnte er aus (n, e) auf effiziente Weise die geheime Zusatzinformation (ϕ(n), d) berechnen. Es wird aber vermutet, dass es wesentlich schwieriger ist, eine Zahl zu faktorisieren, als zu entscheiden, ob sie Primzahl ist. Daher liefern effiziente Primzahltests für Zahlen, die keine Primzahlen sind, im Allgemeinen keine Teiler. Wir wollen jedoch nicht tiefer in die Diskussion über die Sicherheit des RSA-Systems einsteigen, da dieses System in unserem Kontext seine Aufgabe, die Beschäftigung mit Problemen der elementaren Zahlentheorie für sehr große Zahlen zu motivieren, bereits erfüllt hat. 11.2 Potenzen, multiplikative Inverse und größte gemeinsame Teiler Wir benutzen nun die arithmetischen Operationen Addition, Subtraktion, Multiplikation und ganzzahlige Division als Elementaroperationen. Dann ist auch die Berechnung von a mod m in konstanter Zeit möglich. In m können wir also Addition, Subtraktion und Multiplikation als Elementaroperationen ansehen. Im RSA-System müssen Potenzen xn mod m für sehr große n berechnet werden. Algorithmus 11.2.1: 0.) Es ist x1 = x. 1.) Falls n gerade, berechne rekursiv y = xn/2 mod n. Dann ist xn = y 2 mod n. 2.) Falls n ungerade, berechne rekursiv y = xn−1 mod n. Dann ist xn = xy mod n. Satz 11.2.2: Es sei (nk−1 , . . . , n0 ) für k = dlog(n + 1)e die Binärdarstellung von n ≥ 1 und A(n) die Zahl der Einsen in der Binärdarstellung von n. Dann berechnet Algorithmus 11.2.1 xn mit k + A(n) − 2 ≤ 2dlog(n + 1)e − 2 Multiplikationen. Beweis: Wir führen den Beweis durch Induktion über n. Für n = 1 gilt die Behauptung offensichtlich. Für n ≥ 2 ist k ≥ 2. Falls n gerade, ist n0 = 0, und n/2 hat die Binärdarstellung (nk−1 , . . . , n1 ). Nach Induktionsvoraussetzung wird xn/2 mit (k − 1) + A(n) − 2 Multiplikationen berechnet. Zur Berechnung von xn wird eine Multiplikation mehr durchgeführt. Falls n ungerade, ist n0 = 1 und n − 1 gerade. Nach Induktionsvoraussetzung wird xn−1 mit k + (A(n) − 1) − 2 Multiplikationen berechnet. Zur Berechnung von xn wird eine Multiplikation mehr durchgeführt. 2 117 Im RSA-System besteht die Chiffrierung und Dechiffrierung jedes Nachrichtenblocks in der Berechnung einer Potenz me mod n oder y d mod n. Dabei können e und d z.B. 1024stellige Binärzahlen sein. Nach Satz 11.2.2 ist die Chiffrierung und Dechiffrierung mit je höchstens 2048 Multiplikationen und Divisionen mit Rest von Zahlen der Länge 2048 möglich. Hier zeigt sich auch, dass Multiplikations- und Divisionsverfahren, die erst für große Zahlen andere Verfahren schlagen, ihre Bedeutung in der Praxis haben. Der in Kapitel 11.6 zu besprechende Primzahltest wird als Teilproblem für die zu testende Zahl n und Zufallszahlen a entscheiden, ob sie einen gemeinsamen Teiler haben. Dies geschieht durch die Berechnung des größten gemeinsamen Teilers ggT(a, n) der Zahlen a und n. Die Schulmethode für die ggT-Berechnung geht auf die Primfaktorzerlegung von a und n zurück. Wie schon in Kapitel 11.1 erwähnt, ist die Faktorisierung großer Zahlen eine bisher nicht effizient durchführbare Aufgabe. Allerdings steht mit dem euklidischen Algorithmus seit langem ein effizienter Algorithmus zur Berechnung des größten gemeinsamen Teilers zur Verfügung. Es wird definiert, dass ggT(a, 0) = a ist. Die Hauptidee des Algorithmus ist das folgende Lemma. Lemma 11.2.3: Falls b 6= 0, gilt ggT(a, b) = ggT(b, a mod b). Beweis: Wir erhalten a mod b, indem wir a − ba/bcb berechnen, also indem wir b so oft wie möglich von a subtrahieren. Es genügt daher, für a ≥ b zu beweisen, dass ggT(a, b) = ggT(a − b, b) ist. Falls eine Zahl c zwei Zahlen i und j mit i ≥ j teilt, dann teilt c auch i + j und i − j. Jeder gemeinsame Teiler von a und b teilt somit auch a − b und b, und umgekehrt teilt jeder gemeinsame Teiler von a − b und b auch (a − b) + b = a und b. 2 Algorithmus 11.2.4: (Euklidischer Algorithmus) Eingabe: a, b mit a ≥ b ≥ 1. ggT(a, b) begin c := a mod b. (Dann ist 0 ≤ c ≤ b−1.) if c = 0 then return b else return ggT(b, c). end Satz 11.2.5: Der euklidische Algorithmus berechnet den größten gemeinsamen Teiler von a und b mit O(log(a + b)) arithmetischen Operationen. Die Zahl der wesentlichen Operationen, d.h. die Zahl der Modulo-Operationen, ist nicht größer als blog3/2 (a + b)c. Beweis: Wir weisen zuerst nach, dass bei jedem rekursiven Aufruf von ggT(b, c) gilt, dass (b + c) ≤ 32 (a + b). Wegen c = a − k · b für ein k ≥ 1 folgt einerseits, dass a ≥ b + c. Andererseits ist b = 2b ≥ b+c wegen b ≥ c und somit b ≥ b+c . Aus beiden Ungleichungen zusammen folgt 2 2 2 3 a + b ≥ 2 · (b + c). Sei nun L(a, b) die Anzahl der Modulo-Operationen bei Eingabe a und b. Dann ist L(1, 1) = 1 und L(a, b) = L(b, c) + 1 für a ≥ 2. Mit Induktion über a + b zeigen wir 118 nun, dass L(a, b) ≤ log3/2 (a + b) gilt. Es ist L(1, 1) ≤ log3/2 2 und 2 (a + b) + 1 = log3/2 (a + b). L(a, b) = L(b, c) + 1 ≤ log3/2 (b + c) + 1 ≤ log3/2 3 2 Mit einer Erweiterung des euklidischen Algorithmus kann man auch multiplikative Inverse berechnen. Algorithmus 11.2.6: (Erweiterter euklidischer Algorithmus) Eingabe: Natürliche Zahlen a ≥ b ≥ 1 mit ggT(a, b) = 1. Ausgabe: Das Inverse b−1 mod a. INV(a, b) begin if b=1 then return 1; c := a mod b. c∗ := INV(b, c). ∗ return ( 1−cb a ) mod a. end Satz 11.2.7: Algorithmus 11.2.6 berechnet das Inverse b−1 mod a mit O(log(a+b)) vielen arithmetischen Operationen. Beweis: Zunächst beweisen wir mit Induktion über b, dass tatsächlich das multiplikative Inverse berechnet wird. Falls b = 1 ist, wird die 1 als das Inverse korrekt zurückgeliefert. Sei also b > 1. Die Zahl c ist größer als 0, denn anderenfalls wäre a ein Vielfaches von b und somit wegen b > 1 der ggT von a und b größer als 1. Da ggT(a, b) = ggT(b, c) = 1 sowie c ≥ 1 und b > c ist, ist der Aufruf INV(b, c) zulässig und c∗ ist nach Induktionsvoraussetzung gleich c−1 mod b. Nach der Wahl von c gilt (c − a) ≡ 0 mod b, also c∗ · (c − a) ≡ 0 mod b, also c∗ · c − c∗ · a ≡ 0 mod b, also 1 − c∗ a ≡ 0 mod b. Damit ist 1 − c∗ a Vielfaches von b und somit ist die ∗ Zahl Z := 1−cb a eine ganze Zahl. Diese Zahl Z hat die Eigenschaft Z · b = 1 − c∗ a, also Z · b ≡ 1 mod a. Damit ist Z mod a das multiplikative Inverse von b modulo a. Die Laufzeit weist man wie beim ggT-Algorithmus nach. 2 11.3 Der chinesische Restklassensatz Wenn wir im Bereich der Zahlen 0, . . . , m − 1 korrekt rechnen wollen, können wir in m rechnen. Dies verhindert, dass Zwischenergebnisse zu lang werden. Nach dem chinesischen Restklassensatz können wir unter gewissen Voraussetzungen die Rechnung in m auf Rechnungen in m(i) für kleine m(i) zurückführen. Aus den Ergebnissen in m(i) lässt sich das Endergebnis in m effizient berechnen. 119 Satz 11.3.1: Seien m1 , . . . , mk natürliche Zahlen mit ggT(mi , mj ) = 1 für i 6= j. Seien außerdem natürliche Zahlen a1 , . . . , ak gegeben. Das folgende Gleichungssystem hat genau eine Lösung x mit x ∈ {0, . . . , m1 m2 · · · mk − 1}: x ≡ a1 mod m1 x ≡ a2 mod m2 .. . x ≡ ak mod mk . Beweis: Wir beweisen diesen Satz, indem wir ein Verfahren angeben, das die Lösung x berechnet. Wir können uns auf den Fall 0 ≤ ai < mi für alle i = 1, . . . , k beschränken. Es gibt m1 · · · mk viele solche Gleichungssysteme. Kein x kann zwei solcher Gleichungssysteme erfüllen. Wenn wir also nachweisen, dass für jedes Gleichungssystem mindestens eine Lösung x existiert, dann ist wegen |{0, . . . , m1 m2 · · · mk − 1}| = m1 · · · mk klar, dass jedes Gleichungssystem genau eine Lösung x hat. Wir zeigen die Existenz einer Lösung nun durch Induktion über k. Falls k=1, ist a1 mod m1 eine Lösung. Für den Induktionsschritt nehmen wir an, dass wir eine Zahl L haben, die das folgende Gleichungssystem erfüllt und für die 0 ≤ L ≤ m1 · · · mk−1 − 1 gilt. x ≡ a1 mod m1 x ≡ a2 mod m2 .. . x ≡ ak−1 mod mk−1 . (1) Wir zeigen, wie man eine Zahl L∗ findet, die das Gleichungssystem (1) erfüllt und für die zusätzlich L∗ ≡ ak mod mk und 0 ≤ L∗ ≤ m1 · · · mk − 1 gilt. Da L eine Lösung des Gleichungssystems (1) ist, sind auch alle L+t·m1 · · · mk−1 Lösungen des Gleichungssystems (1). Wir suchen ein t, so dass zusätzlich L + t · m1 · · · mk−1 ≡ ak mod mk gilt. Man wählt ein t mit t ≡ (ak − L) · (m1 · · · mk−1 )−1 mod mk . Diese Zahl existiert, da das Inverse von (m1 · · · mk−1 ) modulo mk existiert, da alle mi relativ prim zueinander sind. Man kann t so wählen, dass 0 ≤ t ≤ mk − 1 ist. Nun ist also L∗ := L + t · m1 · · · mk−1 Lösung des Gleichungssystems (1) und zusätzlich ist L∗ ≡ ak mod mk erfüllt. Wir überprüfen die Größe von L∗ : 0 ≤ L∗ ≤ L + (mk − 1) · m1 · · · mk−1 ≤ (m1 · · · mk−1 − 1) + (mk − 1) · m1 · · · mk−1 = m1 · · · mk − 1. 2 120 Beispiel: Wir wollen das Gleichungssystem x ≡ 1 mod 2 x ≡ 2 mod 3 x ≡ 9 mod 11 lösen. Angenommen, wir kennen schon die Lösung L = 5, die die ersten beiden Gleichungen erfüllt. Wir berechnen: t ≡ (9 − 5) · (2 · 3)−1 mod 11. Das Inverse von 6 modulo 11 ist 2, also erhalten wir t ≡ 4 · 2 mod 11 ≡ 8 mod 11. Damit errechnet sich L∗ = 5 + 8 · (2 · 3) = 53 und wir haben die Lösung 53 des Gleichungssystems gefunden. Der chinesische Restklassensatz wird in der Informatik häufig wie folgt eingesetzt: Angenommen, wir betreiben Arithmetik mit Zahlen im Bereich von 0 bis M für eine große Zahl M . Anstatt mit diesen Zahlen selbst zu rechnen, kann man auch wie folgt verfahren: Wähle Primzahlen p1 , . . . , pr , deren Produkt größer als M ist. Um zum Beispiel zwei Zahlen N1 und N2 miteinander zu multiplizieren, kann man zunächst alle Werte N1 mod pj und N2 mod pj berechnen und diese miteinander multiplizieren. Um zum Beispiel 5 mit 13 zu multiplizieren, würde man die Darstellungen 13 ≡ 1 mod 2 5 ≡ 1 mod 2 13 ≡ 1 mod 3 5 ≡ 2 mod 3 13 ≡ 2 mod 11 5 ≡ 5 mod 11 verwenden, um als Produkt der beiden die Darstellung x ≡ 1 mod 2 x ≡ 2 mod 3 x ≡ 10 mod 11 zu bekommen. Das obige Verfahren würde aus dieser Darstellung den Wert x = 65 berechnen. Die Vorteile, die diese Darstellung manchmal bietet, werden natürlich an diesen kleinen Beispielen noch nicht deutlich. 11.4 Grundlagen aus der Zahlentheorie In diesem Abschnitt wiederholen wir einige Begriffe aus der Zahlentheorie und führen die für den Algorithmus von Solovay und Strassen benötigten Grundlagen ein. Definition 11.4.1: b) a) n ist die Menge der Zahlen {0, . . . , n−1}. ∗ n ist die Menge aller Zahlen aus {1, . . . , n−1}, die teilerfremd zu n sind (und damit ein Inverses modulo n haben). 121 c) Für eine Zahl a ∈ ∗n bezeichnet ordn (a) die Ordnung von a, d.h. das kleinste i ≥ 1, so dass ai ≡ 1 mod n ist. d) Eine Zahl a ∈ ∗n ist ein quadratischer Rest modulo n, Notation a ∈ QR(n), falls die Gleichung x2 ≡ a mod n eine Lösung x ∈ ∗n hat. e) Ein Element g ∈ ∗ n heißt erzeugendes Element von f) Wenn g ein erzeugendes Element von das kleinste i ≥ 0, so dass g i = a ist. ∗ n ∗ n, ∗ n wenn ist, dann ist für a ∈ ∗ n = {g i | i ≥ 0} ist. der Index indexg (a) g) Die Zahl n heißt quadratfrei, wenn in der Primfaktorzerlegung von n keine Primzahl mit einem Exponenten größer als 1 vorkommt. h) Die Zahl λ(m) bezeichnet das kleinste i ≥ 1, so dass für alle a ∈ ∗m gilt, dass ai ≡ 1 mod m ist. λ(·) wird auch manchmal als Carmichael-Funktion bezeichnet. Es ist bekannt, dass ∗ p immer ein erzeugendes Element besitzt, wenn p eine Primzahl ist. Beispiel 11.4.2: 2 ist ein quadratischer Rest modulo 7, da 32 ≡ 2 mod 7. Die Zahl 14 ist quadratfrei, die Zahl 50 nicht. Es ist QR(9) = {1, 4, 7}. 3 ist ein erzeugendes Element von ∗5 , da {3, 32 mod 5, 33 mod 5, 34 mod 5} = {3, 4, 2, 1}. Es ist index3 (4) = 2 in ∗5 . Es ist λ(9) = 6. In der Zahlentheorie kennt man folgende Aussagen für die Carmichael-Funktion λ(·). Satz 11.4.3: a) λ(pe ) = pe−1 · (p − 1), wenn p eine ungerade Primzahl ist und e ≥ 1. b) λ(2) = 1, λ(4) = 2 und λ(2e ) = 2e−2 für e ≥ 3. c) λ(pα1 1 · · · pαr r ) = kgVi=1...r λ(pαi i ) (wenn alle pi Primzahlen sind und pi 6= pj für i 6= j). Hier einige weitere Eigenschaften: Satz 11.4.4: Wenn p ≥ 3 eine Primzahl ist, dann ist |QR(p)| = 1/2 · | ∗ p |. Beweis: Es ist x2 ≡ y 2 mod p ⇔ (x − y) · (x + y) ≡ 0 mod p. Wegen der Nullteilerfreiheit ist dies äquivalent zu x ≡ y mod p oder x ≡ −y mod p. Damit gilt p−1 2 2 QR(p) = {x | x = 1, . . . , p − 1} = x | x = 1, . . . , . 2 2 Satz 11.4.5: Sei a ∈ ∗ n. Es gilt ae ≡ 1 mod n ⇔ e ist Vielfaches von ordn (a). Beweis: Zerlege e = k · ordn (a) + j mit 0 ≤ j < ordn (a). Es ist ak·ordn (a)+j ≡ (aordn (a) )k · aj ≡ 1 · aj mod n. Dies ist genau dann kongruent 1 modulo n, wenn aj ≡ 1 mod n. Da j < ordn (a), folgt aus der Definition der Ordnung, dass j = 0 ist. 2 122 Satz 11.4.6: (Kleiner Satz von Fermat) Sei p eine Primzahl. Für alle a ∈ ap−1 ≡ 1 mod p. ∗ p gilt Man kann den Satz von Fermat benutzen, um für manche Zahlen nachzuweisen, dass sie nicht prim sind. Zum Beispiel kann man mit der effizienten Potenzierungsmethode schnell ausrechnen, dass 250 ≡ 4 mod 51 ist. Aus dem Satz von Fermat folgt sofort, dass damit 51 keine Primzahl sein kann. Die Zahl a = 2 kann man also als Zeugen betrachten, der nachweist, dass n = 51 keine Primzahl ist. Dummerweise gibt es Zahlen, die keine Primzahl sind und für die kein derartiger Zeuge (nach dem kleinen Satz von Fermat) existiert. Dies sind die so genannten Carmichael-Zahlen. Definition 11.4.7: Eine Zahl n ≥ 2 heißt Carmichael-Zahl, wenn n keine Primzahl ist und für alle a ∈ ∗n gilt, dass an−1 ≡ 1 mod n ist. Der Primzahltest von Solovay und Strassen wird wegen der Existenz solcher CarmichaelZahlen auf einer Verschärfung des kleinen Satzes von Fermat basieren. Übrigens ist die kleinste Carmichael-Zahl 561 = 3 · 11 · 17, und unter allen Zahlen, die kleiner als 1012 sind, gibt es gerade mal 8241 Carmichael-Zahlen. Erst 1994 wurde bewiesen, dass es unendlich viele Carmichael-Zahlen gibt. Man kann Carmichael-Zahlen wie folgt charakterisieren: Satz 11.4.8: Eine zusammengesetzte Zahl n ist Carmichael-Zahl genau dann, wenn n − 1 ein Vielfaches von λ(n) ist. Beweis: Die Richtung ⇐“ ist einfach: Wenn n − 1 = k · λ(n), ist an−1 ≡ (aλ(n) )k ≡ ” 1 mod n. Für die andere Richtung sei n Carmichael-Zahl. Aus Satz 11.4.5 folgt, dass λ(n) das kleinste gemeinsame Vielfache aller Ordnungen ordn (a) ist. Da n Carmichael-Zahl ist, gilt für alle a, dass an−1 ≡ 1 mod n, und damit ist n − 1 Vielfaches der Ordnung ordn (a). Also ist n − 1 ein Vielfaches aller Ordnungen ordn (a) und damit Vielfaches des kleinsten gemeinsamen Vielfachen dieser Ordnungen. 2 Die folgende Eigenschaft der Carmichael-Zahlen werden wir später beim Beweis der Korrektheit des Primzahltests benötigen. Satz 11.4.9: Wenn n eine Carmichael-Zahl ist, ist n ungerade und quadratfrei. Beweis: Wenn n > 2 ist, dann ist ordn (n−1) = ordn (−1) = 2, somit ist λ(n) ein Vielfaches von 2. Nach Satz 11.4.8 ist somit auch n−1 gerade, also n ungerade. Angenommen, n ist nicht quadratfrei, dann gibt es eine ungerade Primzahl p mit p2 teilt ” n“. Aus Satz 11.4.3a) folgt, dass p die Zahl λ(n) teilt. Also (Satz 11.4.8) teilt p auch n−1. Die Zahl p kann aber nicht gleichzeitig Teiler von n und n−1 sein, ein Widerspruch. 2 123 11.5 Das Jacobi-Symbol Wir beginnen mit der Definition des Jacobi-Symbols. Definition 11.5.1: (Jacobi-Symbol) i) Für eine Primzahl p und für a ∈ ∗ p ist das Jacobi-Symbol a +1, falls a ∈ QR(p), = −1, falls a ∈ 6 QR(p). p a p wie folgt definiert: ii) Für ungerade Zahlen q ≥ 3 mit der Primfaktorzerlegung q = q1 · · · qk ist das JacobiSymbol aq von a bzgl. q (mit ggT(a, q) = 1) definiert als Produkt der Jacobi Symbole qai , 1 ≤ i ≤ k. Wir beachten, dass bei ii) Primfaktoren mehrfach vorkommen können, also dass qi = qj für i 6= j möglich ist. Beispiel 11.5.2: Es gilt 72 = +1, weil 2 ∈ QR(7) ist, denn 32 = 9 mod 7 = 2 mod 7. 2 Auch ist 3 = −1 einfach einzusehen, da QR(3) = {1} ist. Aus beiden zusammen folgt 2 = −1. 21 Wenn n eine Primzahl ist, wird das Symbol na auch manchmal Legendre-Symbol genannt. Wir werden einen effizienten Algorithmus zur Berechnung des Jacobi-Symbols entwerfen. Der Primzahltest wirdfür die zu testende Zahl n und eine Zufallszahl a mit ggT(a, n) = 1 das Jacobi-Symbol na mit dem effizienten Algorithmus berechnen und dies mit der Zahl n−1 a 2 modulo n vergleichen. Für Primzahlen n liefern beide Rechnungen das gleiche Resultat. Für zusammengesetzte Zahlen hängt es von a ab, ob beide Rechnungen das gleiche Resultat liefern. Wenn also die Ergebnisse verschieden sind, muss n eine zusammengesetzte Zahl sein. Wir listen nun Rechenregeln für das Jacobi-Symbol auf. Auf diesen Rechenregeln wird der effiziente Algorithmus zur Berechnung des Jacobi-Symbols beruhen. b ∗ = ap . Lemma 11.5.3: Für jede ungerade Zahl p und für a, b ∈ p gilt ab p p Ein berühmtes Resultat aus der Mathematik ist das so genannte quadratische Reziprozitätsgesetz. Für dieses Ergebnis gibt es eine Unmenge an verschiedenartigen Beweisen. Wir benötigen das Reziprozitätsgesetz in der folgenden Form. Satz 11.5.4: Es seien p, q ≥ 3 ungerade und ggT(p, q) = 1. q i) pq = (−1)(p−1)(q−1)/4. p ii) 2 q = (−1)(q 2 −1)/8 . 124 Nun haben wir alle Hilfsmittel bereitgestellt, um einen effizienten Algorithmus zur Berechnung des Jacobi-Symbols zu entwerfen. Algorithmus 11.5.5: (Berechnung des Jacobi-Symbols) Input: q ∗ > 1ungerade und p∗ mit ggT(p∗ , q ∗ ) = 1. ∗ Output: s = pq∗ . ∗ p q∗ Setze s := 1; p := p und q := q . Kommentar: Es soll stets = s · pq gelten. while p 6= 1 do begin 1.) p := p mod q. 2.) Falls q mod 8 ∈ {1, 7}, dann t := 1, sonst t := −1. 3.) while p gerade do begin p := p/2; s := t · s; end 4.) Falls p 6= 1, vertausche p mit q und setze s := s · (−1)(p−1)(q−1)/4. end 5.) Output s; ∗ Satz 11.5.6: Algorithmus 11.5.5 berechnet das Jacobi-Symbol pq∗ in O(log(p∗ + q ∗ )) Schritten. ∗ ∗ Beweis: Wir beweisen zunächst die Korrektheit des Algorithmus. In Schritt 2 des Algo 2 2 rithmus gilt t = q . Der Grund dafür ist der folgende: Um zu entscheiden, ob (−1)(q −1)/8 gleich 1 oder gleich −1 ist, muss man nur wissen, ob (q 2 − 1)/8 gerade oder ungerade ist. Da (q 2 − 1)/8 genau dann gerade ist, wenn q 2 − 1 durch 16 teilbar ist, weiß man, dass 2 (−1)(q −1)/8 genau dann 1 ist, wenn q mod 16 ∈ {1, 7, 9, 15} ist. Und dies ist äquivalent zu q mod 8 ∈ {1, 7}. Man braucht also nur die letzten 3 Bits in der Binärdarstellung von q, um t aus q zu berechnen. Mit Hilfe ähnlicher Überlegungen kann man in Schritt 4.) den Ausdruck (−1)(p−1)(q−1)/4 berechnen. ∗ p Wir zeigen induktiv, dass die Beziehung q∗ = s · pq nach jedem Schritt des Algorithmus gilt. Schritt 1: Es sei p̂ ≡ p mod q. Es soll pq = p̂q gelten. Die Gleichungen x2 ≡ p mod q und x2 ≡ p̂ mod q sind äquivalent. Falls q Primzahl ist, folgt pq = p̂q aus Teil i) der Definition des Jacobi-Symbols. Falls q = q1 · · · q von q ist, gilt k die Primfaktorzerlegung p p̂ natürlich p̂ ≡ p mod qi für 1 ≤ i ≤ k. Also folgt qi = qi und daraus nach Definition des Jacobi-Symbols pq = p̂q . Schritte 2 und 3: Wie oben angedeutet ist t = 2q . In Schritt 3 wird dann die Eigen schaft 2x = 2q · xq ausgenutzt. q Schritt 4: p ist nun ungerade. Die Zahl q wird nur in Schritt 4 verändert. Da q zu Beginn ungerade ist, sind p und q vor und daher auch nach Schritt 4 ungerade. Es kann nun 125 Satz 11.5.4 i) in der Form p q = (−1)(p−1)(q−1)/4 q p angewendet werden. Schritt 5: Da p = 1, ist 1 = 1. q ∗ p q∗ =s 1 q . Da 1 ∈ QR(qi ) für alle Primfaktoren qi von q, ist Wenn der Algorithmus stoppt, ist also s = ∗ p q∗ . Wir untersuchen nun die Laufzeit des Algorithmus. Die Summe p + q wird während des Algorithmus nur verkleinert. In Schritt 4 und 1 wird das Paar (p, q) mit p < q durch das Paar (q mod p, p) ersetzt. Dies ist genau ein Iterationsschritt des euklidischen Algorithmus zur Berechnung von ggT(p, q). Da q in Schritt 3 stets ungerade ist, wird der größte gemeinsame Teiler von p und q durch das Abspalten der Faktoren 2 in p nicht verändert. Da nach Voraussetzung ggT(p, q) = 1 ist, würde der euklidische Algorithmus mit p = 0 und q = 1 stoppen. Ein Iterationsschritt vorher ist p = 1und q > 1. Algorithmus ∗ 11.5.5 stoppt in dieser Situation und produziert die Ausgabe pq∗ . Nach Satz 11.2.5 wird die Schleife weniger als blog3/2 (p∗ + q ∗ )c-mal durchlaufen. Die Schritte 1, 2 und 4 verursachen jeweils Kosten O(1). In Schritt 3 wird insgesamt (über alle Iterationen betrachtet) höchstens (blog p∗ c + blog q ∗ c)-mal ein Faktor 2 von den Zahlen abgespalten, also sind auch die Gesamtkosten von Schritt 3 durch O(log(p∗ + q ∗ )) beschränkt. 2 11.6 Der probabilistische Primzahltest von Solovay und Strassen Der hier vorgestellte probabilistische Primzahltest wurde von Solovay und Strassen (1977) entworfen. Der Parameter k gibt die Zahl der Tests an, die auf die Eingabezahl angewendet werden. Je höher k gewählt wird, umso geringer wird die Irrtumswahrscheinlichkeit des Algorithmus. Satz 11.6.1: Für jede Primzahl p 6= 2 und für a ∈ ∗p gilt a(p−1)/2 ≡ ap mod p. Beweis: Zwei Beobachtungen vorweg: Da p > 2 und somit ungerade ist, ist (p − 1)/2 eine ganze Zahl. Da p eine Primzahl ist, existiert ein erzeugendes Element g modulo p. Nach dem kleinen Satz von Fermat ist ap−1 ≡ 1 mod p. Die Zahl 1 hat aber modulo p nur a zwei Wurzeln, nämlich 1 und −1 (siehe Beweis zu Satz 11.4.4). Da p = 1 genau dann gilt, wenn a ein quadratischer Rest modulo p ist, reicht es zum Beweis dieses Satzes aus, p−1 zu zeigen, dass a genau dann ein quadratischer Rest modulo p ist, wenn a 2 ≡ 1 mod p ist. 126 Nehmen wir zunächst an, dass a ein quadratischer Rest ist, also a ≡ x2 mod p für ein x ∈ ∗p . Dann ist a Wenn umgekehrt a p−1 2 p−1 2 ≡ (x2 ) p−1 2 ≡ xp−1 ≡ 1 mod p. ≡ 1 mod p gilt, dann ist 1≡a p−1 2 ≡ g indexg (a) p−1 2 ≡ g indexg (a)· p−1 2 mod p. ein Vielfaches von der Ordnung von g sein, also ein Vielfaches Dann muss indexg (a) · p−1 2 von p − 1. Also ist indexg (a) = 2 · k für ein k und somit ist (g k )2 ≡ a mod p. Damit ist a ein quadratischer Rest. 2 Wir beschreiben nun den Algorithmus und beweisen dann seine Korrektheit. Algorithmus 11.6.2: (Algorithmus von Solovay und Strassen) Input: n ≥ 3, k ≥ 1. Output: n ist vermutlich Primzahl“ oder n ist zusammengesetzt“. ” ” Falls n gerade: Output n ist zusammengesetzt“, STOP. ” for i := 1 to k do begin Wähle Zufallszahl a ∈ {1, . . . , n−1} gemäß der Gleichverteilung. Berechne ggT(a, n). Falls ggT(a, n) 6= 1: Output n ist zusammengesetzt“, STOP. ” Berechne b ≡ a(n−1)/2 mod n. Falls b 6≡ {−1, 1} mod n: Output n ist zusammenge” setzt“, STOP. (*) Berechne c := na (das Jacobi-Symbol). (*) Falls b 6≡ c mod n: Output n ist zusammengesetzt“, STOP. ” end Output n ist vermutlich Primzahl“. ” Satz 11.6.3: i) Algorithmus 11.6.2 ist in Zeit O(k log n) und damit für konstantes k in linearer Zeit bezogen auf die Inputlänge durchführbar. ii) Für Primzahlen n liefert Algorithmus 11.6.2 den Output n ist vermutlich Prim” zahl“. iii) Für zusammengesetzte Zahlen n liefert Algorithmus 11.6.2 den Output n ist zu” sammengesetzt“ mit einer Wahrscheinlichkeit von mindestens 1 − (1/2) k . Das folgende Lemma ist die zentrale Aussage, die im Algorithmus von Solovay und Strassen ausgenutzt wird. n−1 Lemma 11.6.4: Sei n ≥ 3 ungerade. Sei E(n) = {a ∈ ∗n | na ≡ a 2 mod n}. Dann gilt E(n) = ∗n ⇔ n ist prim. 127 Beweis: Die Richtung ⇐“ gilt nach Satz 11.6.1. Für die andere Richtung führen wir ” einen Widerspruchsbeweis. Dazu nehmen wir an, dass E(n) = ∗n , aber n zusammenge2 setzt ist. Dann gilt für alle a ∈ ∗n , dass an−1 ≡ na ≡ 1 mod n, und damit ist nach Definition n eine Carmichael-Zahl. Nach Lemma 11.4.9 ist n quadratfrei und wir können schreiben n = p · r für eine Primzahl p und ein r > 1 mit ggT(r, p) = 1. Sei g ein quadratischer Nichtrest modulo p, den es nach Satz 11.4.4 gibt. Nach dem chinesischen Restklassensatz gibt es ein a ∈ n , so dass a ≡ g mod p und a ≡ 1 mod r. Wenn ggT(a, n) > 1 wäre, gäbe es eine Primzahl q, die Teiler von a und n ist. Dann ist entweder q = p oder q ist Teiler von r. Aus q = p folgt a ≡ 0 mod p, aus q|r folgt zusammen mit q|a, dass a ≡ 0 mod q und damit a mod r ein Vielfaches von q ist. Beide Folgerungen sind Widersprüche zur Definition von a. Also ist ggT(a, n) = 1 und somit a ∈ ∗n . Nach der Definition des Jacobi-Symbols ist a a a a g 1 = = · = · = (−1) · (+1) = −1. n pr p r p r n−1 n−1 n−1 Nach Annahme ist na ≡ a 2 mod n, also a 2 mod n ≡ −1 mod n und somit a 2 mod r ≡ −1 mod r, was aber ein Widerspruch zu a ≡ 1 mod r ist. 2 Beweis: (von Satz 11.6.3) i) Wir haben in den Sätzen 11.2.2, 11.2.5 und 11.5.6 gezeigt, dass die einzelnen Schritte des Algorithmus für jedes i in O(log n) Schritten durchführbar sind. ii) Der Output n ist zusammengesetzt“ wird nur erzeugt, wenn n gerade, ggT(ai , n) 6= ” (n−1)/2 6≡ ani mod n für ein i ∈ {1, . . . , k} ist. Für 1 für ein i ∈ {1, . . . , k} oder ai Primzahlen ist (s. Satz 11.6.1) keine dieser Bedingungen erfüllt. iii) Für gerades n ist die Wahrscheinlichkeit, dass der Output n ist zusammengesetzt“ ” erzeugt wird, sogar 1. Falls ggT(ai , n) 6= 1 für eine der Zufallszahlen ai , wird n ebenfalls als zusammengesetzt entlarvt. Ansonsten sind die Zufallszahlen a1 , . . . , ak alle in ∗n enthalten. E(n) kann als die Menge der Zahlen gesehen werden, die als Testzahlen die Eingabe n nicht als zusammengesetzt entlarven. Wir zeigen, dass |E(n)| ≤ | ∗n |/2 für zusammengesetzte Zahlen n ist. Die Wahrscheinlichkeit, dass alle zufällig gewählten Testzahlen a1 , . . . , ak schlecht, also in E(n) enthalten sind, ist dann höchstens (1/2)k . Es ist eine elementare Aussage der Theorie endlicher Gruppen, dass die Ordnung einer Untergruppe (Anzahl der Elemente der Untergruppe) die Ordnung der Gruppe teilt. ∗n ist eine Gruppe bzgl. der Multiplikation mod n. Wir zeigen, dass E(n) eine 128 echte Untergruppe von folgt daraus der Satz. ∗ n ist. Da kein echter Teiler von | ∗ n| größer als | ∗ n |/2 ist, Da n zusammengesetzt ist, folgt aus Lemma 11.6.4, dass E(n) 6= ∗n ist. Bleibt also die (einfache) Aufgabe, zu zeigen, dass E(n) eine Untergruppe von ∗n ist. Aus der Mathematik kennen wir folgendes einfaches Kriterium. Lemma 11.6.5: Eine nichtleere endliche Teilmenge U einer Gruppe G ist genau dann eine Untergruppe, wenn mit allen a, b ∈ U auch ab in U liegt. (n−1)/2 E(n) ist nicht leer = 1 ∈E(n). Sei also a, b ∈ E(n). Es ist (ab) wegen b ab a = mod n. Die letzte Gleichung gilt dabei wegen a(n−1)/2 b(n−1)/2 ≡ n n n Lemma 11.5.3. Also ist auch ab ∈ E(n). 2 Unser probabilistischer Primzahltest kann zusammengesetzte Zahlen entlarven. Die Antwort n ist zusammengesetzt“ ist irrtumsfrei. Für Primzahlen kommt es mit Sicherheit ” zur Aussage n ist vermutlich Primzahl“. Diese Aussage wird bei Tests mit k Zufallszah” len (z.B. k = 100) für zusammengesetzte Zahlen n nur mit der (verschwindend kleinen) Wahrscheinlichkeit (1/2)k erzeugt. Diese geringe Fehlerwahrscheinlichkeit ist für die meisten kommerziellen Anwendungen kryptographischer Systeme tolerabel. Die Tests für die einzelnen Zufallszahlen können natürlich auch parallel ausgeführt werden. Für die legalen Benutzer des RSA-Systems stehen also sowohl für die Schlüsselerzeugung als auch für die Chiffrierung und Dechiffrierung der einzelnen Nachrichtenblöcke effiziente Algorithmen zur Verfügung. Die in diesem Kapitel vorgestellten effizienten Algorithmen für die elementare Zahlentheorie haben lineare Laufzeit bezogen auf die Länge der eingegebenen Zahlen. Für den Primzahltest gilt dies allerdings nur, solange k konstant ist. Wir führen den Primzahltest an zwei Beispielen vor. Die Zahl n = 161 soll darauf getestet werden, ob sie Primzahl ist. Wir wollen mit einer Zuverlässigkeitsquote von 1−2−5 ≈ 97% zufrieden sein und setzen k = 5. Offensichtlich ist 161 nicht gerade. Wir erzeugen zunächst die Zufallszahl a = 2. Der euklidische Algorithmus liefert uns die Aussage ggT(2, n) = 1. Mit Hilfe unseres effizienten Potenzierungsalgorithmus berechnen wir 2(n−1)/2 = 280 ≡ 123 mod 161. Dies ist weder kongruent 1 mod 161 noch kongruent −1 mod 161. Damit ist klar, dass 161 keine Primzahl sein kann. (Allerdings hat uns der Test keinen Teiler von 161 verraten.) Als Nächstes testen wir n = 71 mit der Folge der Zufallszahlen a = 2, 3, 5, 9, 10. Der ggT von a und n ist für alle diese Wahlen gleich 1. Es ergibt sich außerdem, dass für alle diese Zahlen a die Zahl b im Algorithmus gleich 1 ist. Auch für alle Jacobi-Symbole na errechnet sich der Wert 1 für die obigen Werte von a. Wenn unsere Zufallszahlen echte Zufallszahlen wären, könnten wir nun mit gutem Gewissen vermuten, dass 71 eine Primzahl ist. Wäre 71 keine Primzahl, wäre die Wahrscheinlichkeit, unter 5 Zufallszahlen keinen Zeugen für diese Tatsache zu finden, höchstens 1/32. 129 Es gibt eine ganze Reihe von Variationen zum Algorithmus von Solovay und Strassen. Zum Beispiel können wir in dem Algorithmus die beiden mit (*) markierten Zeilen weglassen, wenn n ≡ 3 mod 4 ist. Wir brauchen dann also nicht mehr das Jacobi-Symbol zu berechnen. Den Grund dafür wollen wir hier skizzieren: n−1 Sei G(n) := {x ∈ ∗n | x 2 ≡ ±1 mod n}. Die Menge G(n) ist eine Untergruppe von (Beweis ähnlich wie vorhin) und außerdem gilt: Wenn n ≡ 3 mod 4 ist, dann gilt: G(n) = ∗ n ∗ n ⇐⇒ n ist prim. Beweis: Die Richtung ⇐“ folgt aus Satz 11.6.1. ” Wenn n nicht prim ist, dann folgt zunächst, daß n eine Carmichael-Zahl ist. Damit ist n quadratfrei und wir können n = n1 · n2 schreiben für zwei Zahlen n1 , n2 ≥ 3 mit ggT(n1 , n2 )=1. Nach dem chinesischen Restklassensatz können wir ein a aus ∗n wählen, für das a ≡ −1 mod n1 und a ≡ 1 mod n2 gilt. n ist kongruent 3 mod 4, also ist (n − 1)/2 n−1 n−1 ungerade, also ist a 2 ≡ −1 mod n1 und a 2 ≡ 1 mod n2 . Da n1 , n2 6= 2 sind (n ist n−1 ungerade), ist a 2 6≡ ±1 mod n, also ist a nicht in G(n). Wir nutzen hierbei aus, dass n−1 n−1 n−1 aus a 2 ≡ 1 mod n1 n2 folgt, dass auch a 2 ≡ 1 mod n1 und a 2 ≡ 1 mod n2 ist, und dies analog gilt, wenn die 1 durch −1 ersetzt wird. Wir erhalten also, dass G(n) eine echte Untergruppe von ∗n ist. 2 Die Forderung in der obigen Aussage, dass n ≡ 3 mod 4 sein sollte, ist übrigens essentiell, denn zum Beispiel für die Zahl n = 1729 = 7 · 13 · 19, die nicht kongruent 3 modulo 4 ist, gilt G(n) = ∗n . Es ist vielleicht eine gute Übungsaufgabe, dies nachzuweisen, ohne für 1729−1 alle Elemente a aus ∗n den Wert a 2 auszurechnen. 130