stammvorlesung effiziente algorithmen ss 2002

Werbung
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
Herunterladen