PDF-Version - TU Dortmund, Informatik 2

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

= [a0 + a4 w ] + w
2j
CD
4j
Welche Punkte haben den kleinsten Abstand?
. – Seite 617/726
. – Seite 618/726
Wie ist hier divide-and-conquer möglich?
Zum Aufwärmen: n Punkte auf der Geraden, d-dimensional
für d = 1.
(Natürlich könnte man das Problem über Sortieren lösen.)
Problemstellung ganz einfach zu verstehen.
Motivation: Mindestabstände einhalten, z. B. auf Chip.
Auch hier:
n2
−→
O(n log n).
n/2 Punkte
n/2 Punkte
min. Abstand δ2
min. Abstand δ1
trivial
δ 0 = min{δ1 , δ2 }
δ0
δ0
≤ 1 Punkt pro Menge
Das Problem in der Mitte ist „fast“ 0-dimensional.
. – Seite 619/726
. – Seite 620/726
Nun d = 2, Zerlegung der Punkte in zwei Hälften gemäß
x-Koordinate.
Löse Teilprobleme: δ1 und δ2 , δ 0 = min{δ1 , δ2 }.
C
Algorithmus für n = 2k :
– Sortiere Punkte p1 = (x1 , y1 ), . . . , pn = (xn , yn ) bezüglich
x-Koordinate, nach Umnummerierung
x1 ≤ x 2 ≤ · · · ≤ x n .
O(n log n)
D
δ0
X
p
δ0
A
– Iterativer Aufbau
Rechteck ABCD kann
höchstens 6 Punkte
enthalten.
Runde 1: Betrachte n/2 Punktmengen der Größe 2:
(p1 , p2 ), (p3 , p4 ), . . . , (pn−1 , pn ).
..
.
B
Runde i: Betrachte n/2i Punktmengen der Größe 2i :
(p1 , . . . , p2i ), (p2i +1 , . . . , p2·2i ), . . .
M − δ0
M
M + δ0
Dieser schmale vertikale Streifen ist „fast“ 1-dimensional.
. – Seite 621/726
Ziel nach Runde i:
– Punkte jeder Punktmenge der Größe 2i sind in einem
Array bzgl. y -Koordinate sortiert.
– Für Punkte jeder Punktmenge der Größe 2i ist der
Minimalabstand zweier Punkte bekannt.
. – Seite 622/726
Sei Runde i − 1 erfolgreich beendet.
Implementierung von Runde i in Zeit O(n), dann
Gesamtzeit O(n log n), da es k = log n Runden gibt.
Genügt zu zeigen:
P1 mit 2i−1 Punkten
i = 1:
P2 mit 2i−1 Punkten
– Die beiden Punkte jeder Punktmenge werden bzgl.
y -Koordinate verglichen.
→ P1 ∪ P2 mit 2i Punkten in Zeit O(2i ).
Setze m = 2i−1 .
– Der Abstand der beiden Punkte wird berechnet.
→ O(n)
. – Seite 623/726
. – Seite 624/726
Sortierung bzgl. y -Koordinate mit Reißverschlussverfahren,
da Sortierungen für P1 und P2 vorliegen.
→ O(m).
P1
P2
δ0
δ0
Nur Punkte p1 ∈ P1 ∩ S
und p2 ∈ P2 ∩ S interessieren.
Was bleibt? Minimaler Abstand in P1 ∪ P2 .
Bekannt: δ1 minimaler Abstand in P1 ,
δ2 minimaler Abstand in P2 ,
δ 0 = min{δ1 , δ2 }.
Gesucht: δ = min({δ 0 } ∪ {d(p1 , p2 ) | p1 ∈ P1 , p2 ∈ P2 }).
Diese Punkte können aus
den nach y -Koordinaten sortierten
Folgen herausgefiltert werden → O(m).
. – Seite 625/726
|
{z
Streifen
S
}
. – Seite 626/726
– Sei uj−1 bearbeitet, dabei vs letzter Punkt mit
y(vs ) ≤ y(uj−1 ) + δ 0 .
– Durchlaufe Liste der Punkte in P1 ∩ S nach
aufsteigender y -Koordinate, nenne sie u1 , u2 , . . . , ul
(l ≤ m, l m möglich).
– Betrachte Liste der Punkte in P2 ∩ S nach aufsteigender
y -Koordinate: v1 , . . . , vr .
– Für u1 finde alle Punkte vi mit
y(u1 ) − δ 0 ≤ y(vi ) ≤ y(u1 ) + δ 0 .
Berechne Abstand von u1 und vi und aktualisiere δ 0
gegebenenfalls.
– Für uj sind nur die Punkte ab vs−5 interessant.
(Nur ≤ 6 Punkte im interessanten Bereich.)
– Finde größtes s0 mit y(vs0 ) ≤ y(uj ) + δ 0 , berechne
Abstände zu uj und aktualisiere δ 0 gegebenenfalls.
Korrektheit: klar.
Rechenzeit: O(m), da wir pro uj -Knoten nur ≤ 6 Schritte
rückwärts gehen, gibt es ≤ 6m Rückwärtsschritte und damit
≤ 7m Vorwärtsschritte (amortisierte Analyse).
. – Seite 627/726
. – Seite 628/726
Beispiel: Rechteckmaßproblem
5.10 Sweepline Technik
Probleme der algorithmischen Geometrie haben viele
Anwendungen, besonders in graphischen Systemen.
9 Rechtecke →
18 x-Koordinaten
18 y -Koordinaten
Dazu gehört auch Kap. 5.9.
Hier eine allgemeine Technik für zweidimensionale
Probleme.
Sweepline ist vertikal, überstreicht die Ebene von links
nach rechts, hält an Ereignispunkten.
(Sweepplane für dreidimensionale Probleme.)
Berechne die überdeckte Fläche (nicht die Summe der
Rechteckflächen).
. – Seite 629/726
Ereignispunkte: x-Koordinaten, wo Rechtecke beginnen
oder enden.
Also: Wir wollen l1 , . . . , l2n−1 berechnen.
Dann nur noch: Alle li · (xi+1 − xi ) summieren.
Also 2n Ereignispunkte bei n Rechtecken.
Sortiere diese, so dass x1 ≤ x2 ≤ · · · ≤ x2n .
. – Seite 630/726
O(n log n)
Wie sieht das Problem zwischen zwei Ereignispunkten aus?
xi
xi+1
Informationen aus der Berechnung von li−1 nutzen.
Die Änderungen sind ja gering.
Entweder wird ein Rechteck aktiv oder passiv.
Allerdings könnten Teilbereiche bereits aktiv sein oder aktiv
bleiben.
Länge li der Abschnitte der
Sweepline, die in Rechtecken
liegen, ist konstant.
Also Fläche der Rechtecke in
diesem Bereich: li · (xi+1 − xi ).
Sweepline
. – Seite 631/726
. – Seite 632/726
Zusatzinformationen an v für [i, j]:
Verwende Segmentbaum mit Zusatzinformationen.
– Sortiere y -Koordinaten, wo Rechtecke beginnen oder
enden.
y1 ≤ y2 ≤ · · · ≤ y2n .
O(n log n)
– Früher bei Segmentbäumen Grundmenge [1, n],
Intervalle an den Blättern [i, i],
hier [1, 2n], Aufteilung in angrenzende Bereiche
[1, n] und [n + 1, 2n], an den Blättern [i, i + 1].
Dabei steht [i, j] für [yi , yj ].
– c(v) := Anzahl aktiver Rechtecke, bei deren Zerlegung
im Segmentbaum das Intervall [yi , yj ] entsteht.
– m(v) := Maßzahl an v , Länge des Abschnittes der
Sweepline von yi bis yj , die auf aktive
Rechtecke trifft und bei deren Zerlegung
v erreicht wird.
– Die Wurzel wird für alle Rechtecke erreicht.
Dort steht nach Bearbeitung von xi der Wert li .
. – Seite 633/726
. – Seite 634/726
Update der m-Werte:
Initialisierung:
Alle c(v) = 0 und alle m(v) = 0.
Situation vor dem Erreichen von x1 .
– Betrachte nur Knoten, die bei Bearbeitung des
aktiv/passiv werdenden Rechtecks erreicht werden.
O(n)
– Blatt v = [j, j + 1]:
Update der c-Werte:
– Ein Rechteck wird aktiv:
Erhöhe c-Wert an allen Knoten, die zur Zerlegung des
aktivierten Rechtecks bzgl. y -Koordinate gehören, um 1.
– Ein Rechteck wird passiv:
Analog, nur wird der c-Wert um 1 gesenkt.
O(log n)
m(v) :=
(
0
, falls c(v) = 0,
yj+1 − yj , falls c(v) ≥ 1.
– Innerer Knoten v = [i, j]:
(
m(v1 ) + m(v2 ) , falls c(v) = 0,
m(v) :=
, falls c(v) ≥ 1.
yj − y i
Dabei sind v1 und v2 die Kinder von v .
Updating bottom-up nach Update der c-Werte.
Insgesamt O(n log n).
. – Seite 635/726
O(log n)
. – Seite 636/726
5.11 Analyse von Spielbäumen
Wir behandeln die Basistechnik für alle Programme für
Spiele wie Schach, Go, Dame, . . .
Basis:
Alice zieht
Bob zieht
– Es spielen zwei Personen: Alice und Bob. Keine
Zufallsentscheidungen im Spiel (Karten mischen).
Alice zieht
– Vollständige Information bei Alice und Bob (keine
Karten, die Alice kennt, aber Bob nicht).
Bob zieht
– Bei beliebigen Spielverläufen gibt es eine maximale
Zugzahl, nach der das Spiel endet. (Bei Schach durch
Positionswiederholungsregel erzwungen.)
Alice zieht
5 8 0 1−4 4 −1 2 −2 −3 4 3 1 0 −7−1 −2 −4 1 2 3 4 2 1 −1 0 −2 −3 −4 0 0 2
– Keine Kooperation: Alice erhält, was Bob zahlt, und
umgekehrt.
→ Spiel durch endlichen Baum beschreibbar.
. – Seite 637/726
Bottom-up-Analyse
. – Seite 638/726
Man muss den Gegner/die Gegnerin für ziemlich blöd
halten, wenn man ein anderes Verhalten erwartet/erhofft.
– An den Blättern ist alles entschieden:
Spielwert s(v) = Auszahlungsbetrag.
Also ist v wie ein Blatt.
Wir wissen, dass Alice s(v) an Bob zahlen wird.
– Innerer Knoten, bei dem alle Kinder Blätter sind:
Bottom-up erhält jeder Knoten seinen Wert.
Wert des Spieles := Wert an der Wurzel.
v
−4 10 −2 5
Betrag, den
Alice an Bob
zahlt
Alice am Zug:
Zug 1 optimal, s(v) = −4.
Alice-Knoten sind min-Knoten.
Optimale Strategien:
Alice zieht zum Kind mit kleinstem Wert.
Bob zieht zum Kind mit größtem Wert.
v
−4 10 −2 5
Bob am Zug:
Zug 2 optimal, s(v) = 10.
Bob-Knoten sind max-Knoten.
. – Seite 639/726
. – Seite 640/726
0
Rechenzeit: O(Baumgröße), also linear.
Das ist zu langsam.
0
−1
5
0
−1
0 −4 −1 −3
5
Man möchte gewisse Teilbäume gar nicht erst erzeugen
und dabei wissen, dass keine optimale Strategie verpasst
wird.
1
3
0
3
−1
1
0 −7 −4
1
3
1
3
−1
1 −1 −3 −4
Schach: Der Baum existiert nur „implizit“, er hat mehr
Knoten als Atome im Weltall sind.
0
0
Praktische Programmierung: In manchen Situationen wird
abgebrochen und ein Spielwert geschätzt.
Daher gewinnen Computer nicht immer und verlieren sogar
manchmal.
. – Seite 641/726
Wenn wir manche Teilbäume mit Gewissheit abschneiden
können, können wir die Rechenzeit in spannenderen
Baumteilen verbringen.
Vorläufige Spielwerte s∗ (v) mit folgenden Eigenschaften:
–
s∗ (v)
. – Seite 642/726
max
v
min
s∗ (v) = 20
vi
s∗ (vi ) = 10
⇒ s(v) ≥ s∗ (v) ≥ s∗ (vi ) ≥ s(vi ).
Der wahre Wert von s(vi ) interessiert nicht.
Die weitere Betrachtung von T (vi ) kann eingestellt werden.
Bob geht auf keinen Fall von v zu vi .
≤ s(v) für Max-Knoten, Initialisierung: −∞.
– s∗ (v) ≥ s(v) für Min-Knoten, Initialisierung: +∞.
Analog:
min
Initialisierung erst, wenn der Knoten betrachtet wird.
max
v
s∗ (v) = 10
vi
s∗ (vi ) = 20
⇒ s(v) ≤ s∗ (v) ≤ s∗ (vi ) ≤ s(vi ).
Alice geht nicht von v zu vi .
. – Seite 643/726
. – Seite 644/726
Beispiel
Die Strategie heißt α-β -Pruning.
v1
Alice
Die anfänglichen
Bob
Abschneiden von Teilbäumen
s∗ -Werte
lassen kein Pruning zu.
v2
Wie können wir ein Updating realisieren?
max
min
v
s∗ (v) = 10
vi
s∗ (vi ) = 20
Alice, also Max-Knoten
Bob weiß, dass er sich bei
Zug 2 s(vi ) sichern kann:
s∗ (v) := max{s∗ (v), s∗ (vi )}.
v5
v3
v6
2
1
v7
6
1
v4
v8
8
3
v9
5
2
Bob, also Min-Knoten
v10
3
4
Alice, also Max-Knoten
2
6
An min-Knoten v mit Kind vi : s∗ (v) := min{s∗ (v), s∗ (vi )}.
. – Seite 645/726
. – Seite 646/726
– Linkes Kind von v8 , s∗ (v8 ) = 5, s∗ (v8 ) > s∗ (v3 ), rechtes
Kind muss betrachtet werden.
DFS-Traversierung (links vor rechts)
– Auswertung von v2 , s(v5 ) = 2, Zug 1 an v5 optimal,
s∗ (v2 ) = 2.
– Linkes Kind von v6 , s∗ (v6 ) = 6.
– s∗ (v6 ) ≥ s∗ (v2 ) → Pruning von T (v6 ) (nur ein Knoten
nicht betrachtet, hätte aber ein riesiger Teilbaum sein
können), Zug 1 an v2 optimal, s(v2 ) = 2, s∗ (v1 ) = 2.
– Auswertung von v7 , s(v7 ) = 8, Zug 1 an v7 optimal,
s∗ (v3 ) = 8.
– Rechtes Kind von v8 , s(v8 ) = 5, Zug 1 an v8 optimal
→ s(v3 ) = 5, Zug 2 an v3 optimal
→ s∗ (v1 ) = 5.
– Auswertung von v9 , s(v9 ) = 4, Zug 2 an v9 optimal,
s∗ (v4 ) = 4, s∗ (v4 ) < s∗ (v1 ) → Pruning von
T (v4 ) → s(v1 ) = 5, Zug 2 an v1 optimal.
– Spiel vollständig analysiert.
. – Seite 647/726
. – Seite 648/726
5.12 Randomisierte Suchheuristiken
Wie das?
Bisher: Gute Algorithmen basieren auf Strukturanalyse des
Problems.
Nun:
Strukturanalyse nicht möglich.
Warum?
– Wir kennen die Menge möglicher Lösungen, können
aber die zugehörigen Werte nicht analysieren:
x → f (x) in einer Black Box,
genauer: Experiment oder Simulation eines
Experimentes.
Gibt es so etwas wirklich?
– Mangelnde Ressourcen (Zeit, Geld, Expertise).
– In vielen Gebieten eher der Regelfall, z. B.
– Optimierung technischer Systeme,
– Optimierung von Biokatalysatoren,
– Optimierung von Grammatiken für Phänomene
verschiedener Sprachen, . . .
– Kenntnisse über das Problem fehlen.
. – Seite 649/726
Ziel: Randomisierte Suchstrategien auf verschiedenen
Suchräumen, die bei vielen praktischen Problemen
recht erfolgreich sind.
Ausblick: Verknüpfung randomisierter Suchheuristiken mit
teilweiser Strukturanalyse zu hybriden
Algorithmen.
Hier nur Grundprinzipien allgemeiner Suchheuristiken.
. – Seite 650/726
Suchraum S , z. B. S = {0, 1}n (KP)
S = Menge aller Permutationen auf
{1, . . . , n} (TSP).
f : S → R (hier zu maximieren)
Wähle nach Zufallsverteilung a1 ∈ S , Information f (a1 ).
Allgemein:
Bekannt a1 , f (a1 ), . . . , at−1 , f (at−1 ).
Wähle in Abhängigkeit von dieser Information
Zufallsverteilung auf S , damit erzeuge at , Information f (at ).
. – Seite 651/726
. – Seite 652/726
Selbst wenn optimale Lösung erzeugt wurde, kein Beweis
dafür (im Gegensatz zu bisherigen Algorithmen).
Randomisierte lokale Suche (Randomized Local Search,
RLS)
– Nachbarschaftsbegriff, z. B. Hammingabstand auf
{0, 1}n :
H(a, b) = #{i | ai 6= bi }, dann N (a) = {b | H(a, b) ≤ d}.
Üblich: Informationsreduktion.
Speichere nicht alle betrachteten (a, f (a))-Paare.
Aktuelle Population aktiver Suchpunkte.
Zusätzlich im Hintergrund: Bester bisher gefundener
Suchpunkt.
– Populationsgröße 1.
– Initialisierung gemäß Gleichverteilung.
– Aktueller Suchpunkt x,
erzeuge y ∈ N (x) gemäß Gleichverteilung.
Abbruchkriterium:
– Anzahl erzeugter Suchpunkte.
– y ersetzt x genau dann, wenn f (y) ≥ f (x).
– Kein Fortschritt mehr in vielen Runden.
..
.
. – Seite 653/726
. – Seite 654/726
Metropolis Algorithmus
– Lokale Optima können nicht verlassen werden.
– Wie RLS, lokaler Suchoperator und Populationsgröße 1.
– Ausweg: Multistartstrategien.
– Anderer Selektionsmechanismus, um lokale Optima
verlassen zu können.
Eine problemspezifische Nachbarschaft beim TSP:
– Temperaturparameter T ≥ 0.
k -Opting: Zerschneide aktuelle Tour an k zufällig
ausgewählten Stellen und setze die k Teile
der Tour in zufälliger Reihenfolge zusammen.
– f (y) ≥ f (x): y ersetzt x.
f (y) < f (x): y ersetzt x mit Wahrscheinlichkeit
e−(f (x)−f (y))/T .
– T = 0: RLS, T = ∞: rein zufällige Suche.
. – Seite 655/726
. – Seite 656/726
Evolutionäre Algorithmen
Simulated Annealing
(Analogie zur Herstellung reiner Kristalle)
– Wie Metropolis Algorithmus, aber:
– Sinkende Temperaturen T (t).
– Z. B. T (t) = αt−1 T (1) mit α < 1 oder
Temperaturabsenkung in Abhängigkeit von der Anzahl
verbessernder Schritte.
– Idee:
Zuerst von kleinen Hügeln absteigen und höhere Berge
suchen, später mehr Erklimmen des betrachteten
Berges.
– Globalere Suchoperatoren (Mutation), z. B. auf {0, 1}n :
x → x0
Prob(x0i = xi ) = 1 − p,
Prob(x0i = 1 − xi ) = p,
üblicher Wert p = 1/n.
H(x, x0 ) = d → Prob(x → x0 ) = pd (1 − p)n−d
für p = 1/n
(1/n)d (1 − 1/n)n−d ≥ (1/n)d · e−1 .
W.keit, dass k Bits flippen, ungefähr k!1 · e−1 .
In den meisten Schritten relativ lokale Suche, aber ab
und zu große Sprünge.
. – Seite 657/726
– Populationsgröße µ ≥ 1.
. – Seite 658/726
– Nichtelitäre Selektion:
Positive Auswahlwahrscheinlichkeit für alle,
bessere Chancen bei größerer Fitness.
– Innerhalb einer Generation werden λ Kinder erzeugt.
– Selektion, welche Suchpunkte aus der Population
Eltern werden.
– Populationen neigen zur Klumpenbildung, d. h. Verlust
an Diversität.
– Selektion, wie aus Kindern und Eltern die Population
der nächsten Generation gebildet wird.
– Diversitätserhaltende Maßnahmen:
– Inselmodell (sich unabhängig entwickelnde
Populationen mit seltenem Individuenaustausch).
– Fitness Sharing (Fitness sinkt, wenn viele andere
Individuen kleinen Abstand haben).
– Elitäre Selektion (survival of the fittest):
Wähle die µ Individuen mit der größten
Fitness (f -Wert).
(µ + λ): Auswahl aus Eltern und Kindern.
(µ, λ): Auswahl nur aus den Kindern.
. – Seite 659/726
. – Seite 660/726
Einpunkt-Rekombination (One-point crossover)
Evolutionsstrategien:
Mutation als wesentlicher Suchoperator.
x
Genetische Algorithmen:
Rekombination (Kreuzung, Crossover) als
wesentlicher Suchoperator.
y
zufälliger Schnitt
Formal: Mutation: 1 Elter,
Rekombination: ≥ 2 Eltern.
Gleichförmige Rekombination (Uniform crossover)
x
xi
y
yi
Münzwurf, ob xi oder yi gewählt wird.
. – Seite 661/726
. – Seite 662/726
Dies sind nur Grundvarianten.
Alles ist erlaubt, wenn es erfolgreich ist.
– Analyse bei echten Black-Box-Problemen unmöglich.
Analyse randomisierter Suchheuristiken?
– Analyse erhöht Intuition, bei welchem Problemtyp
welche Varianten geeignet sind.
– Analyse bei strukturierten Problemen möglich.
– Erwartete Zeit, bis optimaler (genügend guter)
Suchpunkt evaluiert wird.
– Analyse unterstützt (wie stets) den Entwurf besserer
Algorithmen.
– Erfolgswahrscheinlichkeit, also Wahrscheinlichkeit, dass
in t(n) Schritten optimaler Suchpunkt evaluiert wird.
. – Seite 663/726
. – Seite 664/726
Prüfungen
Zu einem algorithmischen Problem gehören
– Alles aus dem Skript kann drankommen.
– Problemstellung,
– Vorbereitungsstrategie
für jedes Unterkapitel einen Kurzvortrag einüben
(∼ 4 Min.).
– Motivation,
„hierarchisch lernen“
– Lösungsstrategie,
– die „neuen“ oder „originellen“ Ideen,
– der Algorithmus,
Essentials
– der Korrektheitsbeweis,
– die Analyse,
auch der Ansatz zu den Rechnungen, aber nicht die
Rechnungen,
wichtige Ideen
Details
– die Angabe der Laufzeit.
Die Schwerpunkte sind von Problem zu Problem
verschieden.
. – Seite 665/726
. – Seite 666/726
Kontrollstrategie
– erstelle Stapel von Karteikarten mit allen Unterkapiteln /
Algorithmen / Datenstrukturen / Strategien / . . . ,
Eine kleine Auswahl möglicher Fragen
1. Was ist der Unterschied zwischen der uniformen und
logarithmischen Zeitkomplexität?
– mische den Stapel,
– ziehe Karteikarte zufällig,
– beantworte die Frage laut und in vollständigen Sätzen
und notiere, was du aufschreiben würdest,
– messe die Antwortzeit,
– mache dies möglichst oft mit jemandem, die oder der
zuhört und möglichst etwas davon versteht.
– Prüfungssituation so realistisch wie möglich simulieren
. – Seite 667/726
2. Warum wird meistens die worst case und nicht die
average case Rechenzeit untersucht?
3. Bei QUICKSORT gibt es dieselbe average case
Rechenzeit bei Wahl des ersten Elements als
Pivotelement und bei Wahl eines zufälligen Elements.
Was ist der Unterschied zwischen den beiden „average
cases“?
. – Seite 668/726
8. Vergleiche lineare Listen und Arrays bzgl.
4. Definiere O, Ω, Θ, o, ω und warum ist ihre Verwendung
sinnvoll?
5. Sortiere nach Größenordnung:
1/10
n2 log n, n2 log2 n/(log log n)10 , 2n , 2n/100 , n2 + 3n3/2 ,
n15/8 , n2 / log n, nlog n .
– Suche nach Datum an Position n,
– Suche nach Datum x,
– Einfügen von y hinter x, nachdem x gefunden wurde.
9. Gib Beispiele an, bei denen es sinnvoll ist,
6. Beschreibe die Strategien lineare Suche, binäre Suche
und geometrische Suche mit Vor- und Nachteilen.
7. Wie können spärlich besetzte Matrizen dargestellt
werden und wie werden die Operationen wie Zugriff,
Addition und Multiplikation unterstützt?
– doppelte Verkettung zu haben,
– Zeiger auf das letzte Element zu haben,
– die Größe der Listen zu verwalten.
10. Algorithmus zum topologischen Sortieren.
11. Vergleiche Bitvektordarstellung und geordnete Listen
zur Verwaltung von Mengen.
. – Seite 669/726
12. Beschreibe DFS für gerichtete Graphen.
. – Seite 670/726
16. Welche Operationen werden zur Verifikation von
Schaltkreisen benötigt?
13. Segmentbäume
17. OBDDs – Darstellung und Minimierungsregeln.
– wofür,
– wie,
– Rechenzeit.
18. OBDDs – Synthese mit integrierter Reduktion /
Minimierung.
19. Wie sieht ein OBDD für die Addition / den Multiplexer
aus?
14. UNION-FIND mit Arrays und Listen:
Darstellung der Struktur, FIND, UNION, Analyse.
20. Was sind dynamische Dateien?
15. UNION-FIND mit Bäumen:
Darstellung der Struktur, max. Baumhöhe bei n Daten,
Pfadkomprimierung.
. – Seite 671/726
21. Vergleiche offenes und geschlossenes Hashing.
. – Seite 672/726
22. Beschreibe Strategien zur Kollisionsbehandlung, wieso
unterscheiden sie sich in ihrem Verhalten überhaupt?
23. Was versteht man unter idealem Hashing? Wie sieht
dabei der Ansatz zur Berechnung der erwarteten
Suchzeit aus?
24. Was sind Suchbäume? SEARCH, INSERT, DELETE.
25. Durchschnittliche Tiefe binärer Suchbäume mit n
Daten, die in zufälliger Reihenfolge kommen (Ansatz +
Ergebnis asymptotisch).
26. 2-3-Bäume
Definition, min. und max. Tiefe, SEARCH, INSERT,
DELETE
(hier wie auch sonst: Erklärung nicht nur am Beispiel,
sondern strukturell, aber auch Behandlung von
Beispielen möglich).
27. Das gleiche für B-Bäume.
28. Das gleiche für AVL-Bäume, insbes. Ansatz zur
Berechnung der Maximaltiefe bei n Daten.
Wann welcher Typ von Rotation?
29. Skiplisten, Struktur, durchschnittl. Höhe, Operationen,
Analysetechnik und Anwendung.
. – Seite 673/726
30. Liste die bekannten Sortieralgorithmen mit Vor- und
Nachteilen auf.
– warum ist die Heap Creation Phase effizienter als
die Selection Phase?
– Analyse der worst case Rechenzeiten der
verschiedenen Strategien.
31. Quicksort
– wie kommt das Zerlegungsdatum an die richtige
Position?
– Strategien zur Wahl des Zerlegungsdatums,
– worst case,
– Ansatz zur average case Analyse und Ergebnis.
33. Was ist ein allgemeines Sortierverfahren?
Untere Schranke für worst und average case.
34. Bucketsort: Verfahren und Analyse.
35. Beschreibe effizienten Algorithmus für das
Auswahlproblem mit Analyseansatz.
32. Heapsort
–
–
–
–
. – Seite 674/726
Darstellung des Heaps im Array,
das Rahmenprogramm,
die reheap-Strategien,
warum ist „bottom-up“ besser?
36. Batchersort: Wie geht es und warum klappt es?
Sequentielle und parallele Rechenzeit.
. – Seite 675/726
. – Seite 676/726
37. Beschreibe die folgenden Optimierungsstrategien
allgemein:
–
–
–
–
40. Dynamische Programmierung: Was sind Probleme vom
Intervalltyp?
Greedy Algorithmen.
Dynamische Programmierung.
Divide-and-Conquer.
Branch-and-Bound.
41. Berechnung statischer Suchbäume mit minimaler
erwarteter Zugriffszeit.
38. Gib Beispiele für Greedy-Algorithmen an, die
– stets das Optimum berechnen,
– beliebig schlechte Ergebnisse berechnen können,
– nicht immer optimale, aber nie schlechte Ergebnisse
berechnen.
42. Was sind pseudopolynomielle Rechenzeiten? Gib ein
Beispiel an.
(Dyn. Progr. für das Rucksackproblem)
43. Dynamische Programmierung für das APSP.
44. Algorithmus von Dijkstra: Strategie, Korrektheit,
Implementierung, Analyse, Datenstruktur.
39. Algo. von Kruskal: wozu, wie, Datenstrukturen,
Rechenzeit, warum erhalten wir immer optimale
Resultate?
. – Seite 677/726
. – Seite 678/726
45. Die Module von Brach-and-Bound Algorithmen am
Beispiel des Rucksackproblems.
50. FFT – wozu?
Wie funktioniert es?
46. Divide-and-Conquer
51. Berechnung der nächsten Nachbarn in der Ebene.
52. Was ist die Sweepline-Technik?
R(n) = a · R(n/b) + cn
Wie wirken sich a und b auf die Rechenzeit aus? Und
wie c?
47. Welche Divide-and-Conquer Algos wurden behandelt?
53. Anwendung auf das Rechteckmaßproblem, wie wird der
Segmentbaum verwaltet?
54. Beschreibe das α-β -Pruning.
48. Welche Algos der dyn. Programmierung wurden
behandelt?
49. Beschreibe die wesentlichen Ideen des
Strassen-Algorithmus.
. – Seite 679/726
. – Seite 680/726
55. Was ist das Szenario der Black-Box-Optimierung?
Wie arbeiten randomisierte Suchheuristiken allgemein?
56. Beschreibe
– randomisierte lokale Suche,
– Simulated Annealing,
– evolutionäre Algorithmen.
In Klausuren können auch Beispiele zu behandeln sein!
Keine Aussagen ohne Begründung!
57. Welches Ergebnis der Vorlesung hat dir warum am
besten gefallen / hat dich warum am meisten
überrascht?
. – Seite 681/726
. – Seite 682/726
1.) Beschreibe die lineare Suche, die binäre Suche und die
geometrische Suche nach einem Objekt in einem
geordneten Array der Länge n. Gib die worst case
Suchzeit für alle Strategien in Bezug auf n an und
diskutiere die Abhängigkeit der Suchzeit von i.
2.) Für Quicksort gibt es die beiden Varianten, als
Zerlegungsdatum stets das linkeste Datum oder ein
Datum an zufälliger Position zu wählen. Wie groß ist die
average case Rechenzeit für die beiden Varianten?
Welche Variante ist warum für welche
Anwendungsszenarien besser?
Folgende 12 Klausuraufgaben habe ich tatsächlich in
einer Klausur (Zeit 3 Stunden) gestellt.
3.) In welchem Zusammenhang kommt das Stichwort
Pfadkomprimierung vor und was bedeutet es?
4.) Gib eine vollständige Definition von 2-3-Bäumen an.
. – Seite 683/726
. – Seite 684/726
5.) Gegeben sei folgender AVL-Baum.
6.) Gegeben sei folgende Kostenmatrix für einen
gerichteten Graph, also c(1, 3) = 6, aber c(3, 1) = 2.
Wende den Algorithmus von Dijkstra zur Berechnung
eines kürzesten Weges (nicht nur seiner Kosten) von
Ort 1 zu Ort 5 an. Beschreibe und begründe die
Zwischenschritte. Es ist nicht hilfreich, den Graphen zu
zeichnen.
12
20
6
3
18
9
7
10
Führe nacheinander die Operationen INSERT(8),
DELETE(18) aus. Beschreibe die angewendeten
Regeln und gib die Zwischenschritte an.
1
2
3
4
5
6
7
1
0
2
6
1
10
∞
4
2
1
0
3
1
9
20
1
3
2
1
0
2
3
4
1
4
3
∞
4
0
15
10
4
5
4
0
1
2
0
3
4
6
5
0
2
1
4
0
3
7
6
3
1
1
7
5
0
. – Seite 685/726
7.) Analysiere die worst case Rechenzeit zur Erzeugung
eines Heaps. Ansatz der Rechnung, Begründung des
Ansatzes und Ergebnis genügen, Rechnungen selbst
nicht nötig.
. – Seite 686/726
11.) Wie können mit Hilfe von π -OBDDs Schaltkreise
verifiziert werden und welche Operationen auf
π -OBDDs werden dabei angewendet?
12.) Sei w eine primitive n-te Einheitswurzel und
8.) Warum ist die Query Time in Segmentbäumen
O(log n)?
9.) Beschreibe einen Algorithmus zur Lösung des
Rucksackproblems, der eine optimale
Rucksackbepackung (nicht nur ihren Nutzen) mit
dynamischer Programmierung berechnet und diskutiere
seine Rechenzeit.
f (x) = a0 + a1 x + a2 x2 + · · · + an−1 xn−1 , n = 2k
ein Polynom. Beschreibe die FFT zur Berechnung von
f (w0 ), f (w1 ), f (w2 ), . . . , f (wn−1 ).
10.) Beschreibe den Algorithmus Quickselect zur Lösung
des Auswahlproblems und den Ansatz für die average
case Analyse.
. – Seite 687/726
. – Seite 688/726
5.) Insertionsort und Mergesort sortieren mit sehr wenigen
wesentlichen Vergleichen. Wo liegen die
Hauptprobleme bei ihrer Anwendung?
Beispiele für Kurzaufgaben
1.) Definiere 2-3-Bäume.
6.) Warum stehen die untere Schranke für allgemeine
Sortierverfahren und die obere Schranke für Bucketsort
nicht im Widerspruch?
2.) Was ist die wesentliche Idee, wenn Daten aus dem
Inneren von binären
Suchbäumen/2-3-Bäumen/AVL-Bäumen entfernt
werden sollen?
3.) Was unterscheidet die Durchschnittsbildung bei der
Rechenzeit für das Einfügen beim geschlossenen
Hashing von der Durchschnittsbildung beim Einfügen in
Skiplisten?
7.) Was ist der intuitive Grund dafür, dass die lineare
bottom-up Suche bei der Bottom-up-Variante von
Heapsort im Durchschnitt am effizientesten ist?
4.) Erläutere das Modell des idealen Hashing (keine
Rechnungen).
. – Seite 689/726
. – Seite 690/726
Beispiele für Langaufgaben
Szenarien für Optimierungsprobleme
1.) Analysiere die worst case Tiefe von AVL-Bäumen mit n
Daten (untere Schranke, obere Schranke,
Argumentation, Ansatz und Ergebnis der Rechnung
genügen).
Typische Optimierungsprobleme:
kürzeste Wege, minimale Spannbäume, optimale
Alignments, maximale Matchings, ...
2.) Was versteht man unter Rückwärtsanalyse, wo wurde
sie wie angewendet?
aber auch
Traveling Salesperson Problem, Rucksackproblem,
Scheduling, Hardwareoptimierung, ...
3.) Leite die Rekursionsgleichung für die durchschnittliche
Rechenzeit von Quicksort (könnte auch Clever
Quicksort sein) her.
4.) Analysiere asymptotisch die Heap Creation Phase von
Heapsort.
. – Seite 691/726
. – Seite 692/726
Aber in allen Fällen
Ausweichmöglichkeiten:
Problemstruktur bekannt,
beste bekannte Algorithmen basieren auf Strukturanalyse,
es sind problemspezifische Algorithmen nur für eine
Aufgabe, Analyse der Algorithmen nutzt die
Strukturkenntnisse.
Approximationslösungen,
Lösungen für Teilprobleme,
randomisierte Algorithmen,
teilweise kleine Fehlerwahrscheinlichkeit, ...
. – Seite 693/726
Methodenreservoir immens:
. – Seite 694/726
Anwendungen effizienter Algorithmen in der realen Welt oft
nicht einfach
Greedy Algorithmen, dynamische Programmierung,
Branch and Bound, Divide and Conquer,
lineare Programmierung, randomisiertes Runden,
semidefinite Programmierung, ganzzahlige
Programmierung, ...
Reale Probleme unterscheiden sich von
Lehrbuchproblemen
(die aber oft den Kern der realen Probleme beinhalten)
Praxisproblem
Dies gilt auch für die Analysetechniken.
Trade-off zwischen Effizienz des Algorithmus
und Kosten und Zeit für die Entwicklung des Algorithmus
(oft fehlen auch die algorithmischen Fähigkeiten)
Entwurf und Analyse problemspezifischer Algorithmen –
eines der ältesten und erfolgreichsten Gebiete der
Informatik
. – Seite 695/726
. – Seite 696/726
Daher interessant
Noch schlimmer:
robuste Algorithmen,
Es gibt nicht immer problemspezifische Algorithmen!
die (fast ohne Anpassung) viele verschiedene
Probleme befriedigend effizient lösen.
Wann?
Black-Box Probleme
Derartige Algorithmen übertreffen
problemspezifische Algorithmen nicht,
sind aber leichter verfügbar und anwendbar.
Wir kennen die Problemdimension n und den Lösungsoder Suchraum Sn , aber nicht die zu
optimierende Funktion fn : Sn → R
(zumindest nicht vollständig)
. – Seite 697/726
. – Seite 698/726
Wie kann man dann optimieren?
Gibt es überhaupt Black-Box Probleme?
Information Sampling, d.h.
Das historische Beispiel:
x ∈ Sn
Black Box
Optimierung einer Zweiphasen-Überschalldüse
(Schwefel, 1968)
fn (x)
Maximierung des Wirkungsgrades bei der Umwandlung von
thermischer in kinetische Energie (Staustrahlrohrprinzip)
Experiment
Simulation
Optionen: Form und Länge der Düse
Problem: Effekt von Verwirbelungen nicht verstanden
Randomisierung praktisch unverzichtbar
keine Garantie, dass Ergebnis optimal.
. – Seite 699/726
. – Seite 700/726
Das moderne Beispiel:
Biokatalysatoren – Evolution im Reagenzglas
(Reetz, 2003)
Objekte, z. B. Moleküle, heißen chiral (von Hand (griech.)),
wenn sie mit ihrem Spiegelbild nicht zur
Deckung gebracht werden können, z. B. Hände
. – Seite 701/726
. – Seite 702/726
Chiralität und biologische Wirkung
Enantiomere – chirale Moleküle
die beiden Typen entstehen etwa im Verhältnis 1:1
Enantioselektive Katalysatoren sollen das Verhältnis
in die gewünschte Richtung ändern
→ Optimierungsaufgabe
. – Seite 703/726
. – Seite 704/726
Im SFB 531 behandelte Probleme
Randomisierte Suchheuristiken
– Temperierbohrungsstrategien für Druckguss- und
Spritzgusswerkzeuge,
wie Metropolisalgorithmus, Simulated Annealing,
evolutionäre und genetische Algorithmen sind Antworten
auf beide Problemkreise, also
– Entwicklung von Prozessstrategien zur fünfachsigen
Fräsbearbeitung,
robuste Algorithmen und
– Erhöhung der Fertigungsgenauigkeit beim Profilbiegen,
Algorithmen für Black-Box Probleme
– Belegungsplanung verfahrenstechnischer
Mehrproduktanlagen, ...
. – Seite 705/726
Wie analysiert man randomisierte
Suchheuristiken?
. – Seite 706/726
Analyse randomisierter Suchheuristiken ist undankbar,
Beobachtung
Es ist „einfacher“, maßgeschneiderte Algorithmen zu
analysieren als allgemeine randomisierte Suchheuristiken,
denn . . .
da sie als robuste Algorithmen auf Problemen mit bekannter
Struktur die besten bekannten Algorithmen nicht schlagen
(kein neuer Rekord) und eine Analyse auf Problemen mit
unbekannter Struktur nicht möglich ist.
. . . in der Forschung hat der Algorithmenentwurf zwei Ziele:
Effizienz und Nachweis der Effizienz
. – Seite 707/726
. – Seite 708/726
Was macht man denn dann?
– Analyse des Verhaltens auf typischen Problemen,
selbst wenn es maßgeschneiderte Algorithmen gibt, um
Stärken und Schwächen herauszufinden.
– Herausarbeitung zentraler Fragen, um die
Wirkungsweise randomisierter Suchheuristiken besser
zu verstehen . . .
. . . und „lehrbar“ zu machen.
Wie sehen solche Fragestellungen aus?
Mutation als typischer Suchoperator auf {0, 1}n ,
Parameter pn
(
1 − ai mit W.keit pn
a0 := mut(a)
a0i =
ai
sonst
– statisch: pn konstant, meist pn =
1
n
– dynamisch: festes Veränderungsschema für pn in
Abhängigkeit von der Zeit
– adaptiv: Veränderung gemäß Fortschritt bei der Suche
– selbstadaptiv: pn ist ein Teil der Lösung und wird z. B.
der Evolution unterworfen
. – Seite 709/726
Untersuchte Fragen
. – Seite 710/726
Weitere typische Fragestellungen
– Evolutionäre Algorithmen arbeiten mit Populationen von
Suchpunkten, wie lässt sich die Diversität in der
Population erhalten?
– Wann genügen statische Strategien?
– Wann helfen die erweiterten Strategien?
– Kann man entsprechende Problemeigenschaften
herauskristallisieren?
– Wann ist der Rekombinationsoperator (Crossover)
essenziell?
. – Seite 711/726
. – Seite 712/726
Simulated Annealing
ist die dynamische Variante von MA,
d. h. variable Temperatur T (t),
hier sehr einfache dynamische Form
Die Herausforderung
Erstaunliche Beobachtung:
Für viele Probleme ist MA bei guter Temperatur mindestens
fast so gut wie jede SA-Variante.
T (t) := αt−1 · T (1) mit α < 1
Gilt das immer?
NEIN: – Experimente
– fraktale Fitnessfunktion (Sorkin, 1991)
– maßgeschneiderte Fitnessfunktion (DJW, 2001)
mit den freien Parametern α und T (1).
. – Seite 713/726
Jerrum und Sinclair (1996):
. – Seite 714/726
Wann gilt: SA outperforms MA?
It remains an outstanding open problem to exhibit a natural
example in which simulated annealing with any non-trivial
cooling schedule probably outperforms the Metropolis
algorithm at a carefully chosen fixed value of α.
α=
ˆ T
natural example: minimale Spannbäume (MST)
– SA/MA „wissen nicht“, ob sie optimale Lösung
gefunden haben
– Parameter: Anzahl an Fitnessauswertungen, bis
aktueller Suchpunkt optimal
– erwartete Optimierzeit
kann groß sein, wenn Optimum mit kleiner W.keit
„verpasst“ wird und dann die Temperatur zu klein ist
aber was heißt „outperforms“?
. – Seite 715/726
. – Seite 716/726
Wann ist eine randomisierte Suchheuristik A erfolgreich?
Wenn wir ein Problem nicht gut verstehen?
Polynomielle Anzahl p(m) an Fitnessauswertungen
Erfolgswahrscheinlichkeit s(m)
A heißt
Eine zufallsgesteuerte Suche nach besseren
Lösungen nach dem Vorbild der biologischen Evolution
– erfolgreich, falls s(m) ≥ 1/q(m) für ein Polynom q ,
– hoch erfolgreich, falls es für alle Polynome q eine polyn.
Anzahl p an Rechenschritten gibt mit s(m) ≥ 1 − 1/q(m)
ε
– überwältigend erfolgreich, falls s(m) ≥ 1 − e−Ω(m ) für
ein ε > 0,
Stark vereinfacht:
Evolution ist zufallsgesteuerte Optimierung in einer sich
wandelnden Umwelt und
Evolution ist erfolgreich (?)
– erfolglos, falls s(m) = o(m−k ) für alle k und p.
. – Seite 717/726
. – Seite 718/726
Beispiel
Rezeptänderungen durch Mutation:
Finde das Backrezept für den Lieblingskuchen
(eine neue Kreation) für einen guten Freund
oder eine gute Freundin – wir nennen sie Nadine.
kleine oder lokale Änderungen, z. B.
– etwas mehr Zucker
– etwas weniger Fett
Wir können nicht ausrechnen, wie Nadine ein neuer
Kuchen schmeckt →
– Mandelsplitter statt Schokostreusel
Backen, probieren lassen, Punkte vergeben (Fitness)
. – Seite 719/726
. – Seite 720/726
Neue Kreationen durch Rekombination:
Die meisten Mutationen und Rekombinationen
führen zu ungenießbaren Kuchen!
„mische“ zwei Rezepte,
Arbeite mit einer Population von Rezepten.
vielleicht entsteht aus einem Rezept für
eine Sachertorte und einem Rezept für
eine Kirschtorte mit Sahne ein Rezept für eine
Schwarzwälder Kirschtorte?
Kreiere neue Rezepte, lasse nur einige
neue und alte Rezepte überleben →
Survival of the fittest
. – Seite 721/726
Auch Schwächere (nicht besonders leckere Kuchen)
können in Nischen überleben –
. – Seite 722/726
Ist die Schwarzwälder Kirschtorte
so entstanden ???
Verbesserung durch zeitweise Verschlechterung.
Wie sonst kommt es zu der Kreation
Erdbeeren mit grünem Pfeffer?
Nach genügend vielen Generationen von Rezepten
kann die Supertorte entstehen!
. – Seite 723/726
. – Seite 724/726
Aber sollten wir so bei wichtigen technischen oder
naturwiss. Problemen vorgehen?
– Populationen ermöglichen die Exploration
verschiedener Bereiche des Suchraumes
– Selektion verstärkt die Suche in viel versprechenden
Bereichen
Über sehr viele Probleme wissen wir sehr viel weniger
als über die Qualität von Backrezepten –
– Mutation sucht meist nach lokalen Verbesserungen,
aber auch größere Sprünge, um lokale Optima zu
verlassen
mehr als erstaunliche Erfolge mit
evolutionären Algorithmen!
– Rekombination ist die Hoffnung, gute Teile mittelguter
Suchpunkte zu vereinen
. – Seite 725/726
. – Seite 726/726
Herunterladen