¢¡¤£¦¥¤§©

Werbung
http://www.mpi-sb.mpg.de/~sschmitt/info5-ss01
IS
UN
R
S
SS 2001
E R SIT
S
Schmitt, Schömer
SA
IV
A
Grundlagen zu
Datenstrukturen und Algorithmen
A VIE N
Lösungsvorschläge für das 10. Übungsblatt
Letzte Änderung am 3. Juli 2001
Aufgabe 1
a)
b)
c)
d)
Die mittlere Laufzeit des Algorithmus ist O(n):
Im schlechtesten Fall benötigt der Algorithmus Laufzeit O(n):
Die Erwartete Laufzeit des Algorithmus ist O(n):
Die Laufzeit des Algorithmus ist amortisiert O(n):
a,c) average-case analysis:
Aussage a und d unterscheiden sich nicht von einander. Es ist der im Durchschnitt zu erwartende
Fall gemeint, der average-case. In beiden Aussagen ist also der Erwartungswert der Laufzeit
gemeint. Dies bezieht sich sowohl auf Algorithmen deren Laufzeit nur durch die Problemstellung
bestimmt wird (deterministische Algorithmen), als auch auf solche, die auch auf dem selben Problem unterschiedliche Laufzeiten haben können ( probabilistische Algorithmen wie z.B. Quicksort
). Es wird dabei jedoch, im Gegensatz zu den anderen Aussagen, keine Garantie gegeben, da die
Laufzeit im Einzelfall erheblich ,sowohl nach oben als auch nach unten, abweichen kann.
b) worst-case analysis:
Aussage b bezieht sich auf den schlechtesten Fall, so zu sagen den Super-GAU für diesen Algorithmus, den worst-case. Diese Aussage ist somit eine Garantie, daß der Algorithmus niemals
langsamer ist als O(n).
d) amortized analysis:
Diese Aussage ist ebenfalls eine Garantie, die wie folgt lautet: Wird dieser Algorithmus k mal
(k ∈ N bel.) hintereinander auf die selbe Datenstruktur angewendet, so ist die Gesamtlaufzeit in
O(k*n). Dies bedeutet insbesondere, daß der Erwartungswert der Laufzeit (s. a,c) in O(n) liegt.
Es ist jedoch weiterhin möglich, daß eine einzelne Ausführung des Algorithmus langsamer bzw
schneller ist als O(n). Es wird aber eben garantiert, daß sich das so zu sagen weghebt. Diese
Amortisierte Analyse wird jedoch meistens auf eine ganze Gruppe von Algorithmen, man spricht
dann auch von Operationen, angewendet, die dann miteinander verrechnet werden. (s.Fibonacci
Heaps)
Aufgabe 2
3
6
5
9
7
11
2
13
12
8
heapify:
10
15
2
3
5
6
7
9
11
13
12
8
15
15
erase_min
10
3
5
5
6
13
10
9
7
11
13
12
15
10
8
5
erase_min
8
6
8
6
9
7
13
7
11
10
12
15
12
decrease_key(13,4)
4
6
5
9
11
7
12
5
6
6
9
11
10
12
15
erase_min
8
7
8
9
15
7
11
12
15
decrease_key(8,3)
3
6
5
9
11
7
15
12
12
12
10
10
Aufgabe 3
1. Zunächst wird durch insert() eine einfache doppeltverkettete Liste erzeugt, und ein Zeiger
auf das Minimum gesetzt:
6
5
9
7
13
10
11
2
12
8
15
min
3
2. Bei erase min() findet nun eine Umformung des Heaps statt: Zunächst wird das Minimum
gelöscht, anschließend finden Verschmelzoperationen dergestalt statt, daß nach und nach
Knoten in der Wurzelliste mit gleicher Kinderzahl zusammengefasst werden, bis sich nur
noch Knoten mt unterschiedlicher Kinderzahl in der Wurzelliste einfinden. Zusätzlich wird
der min-Zeiger umgebogen:
min
3
5
7
6
9
13
10
8
12
7
8
min
11
15
3
6
5
13
9
10
11
min
8
3
6
5
7
9
13
12
10
11
15
12
15
3. Beim 2. erase min() wird die 3 gelöscht, und alle ihre Kinder in die Wurzelliste eingetragen.
Anschließend wird wieder verschmolzen. Um die Heap-Eigenschaft (also Vater kleiner als
Kind) zu erhalten, schmelzen wir stets die größere Wurzel unter die kleinere:
min
6
5
7
9
13
8
10
15
12
11
min
6
5
15
9
7
8
13
10
12
11
min
6
5
15
9
8
7
12
13
10
11
4. Bei decrease key( 13, 4) wird zunächst die 13 mit 4 überschrieben. In unserem Fall ist
nun das Kind kleiner als der Elter, eine Heap-Verletzung. Somit schneiden wir die 4 (mit
all ihren Kindern, sind aber keine da!) heraus, und setzten sie in die Wurzelliste. Die 7 hat
nun ein Kind verloren, muß somit markiert werden. Dann noch Umbiegen des min-Zeigers.
min
6
5
15
9
7
8
12
4
10
11
min
6
5
15
9
4
8
7
12
10
11
5. erase min() schneidet nun lediglich die 4 heraus, und setzt den min-Zeiger um:
min
6
5
15
9
8
12
7
10
11
6. decrease key( 8, 3) überschreibt erst die 8 mit der 3, und setzt den Teilbaum mit Wurzel
3 in die Wurzelliste.
min
6
5
15
9
3
7
12
10
11
5
15
9
3
7
min
6
12
10
11
5 hat ein Kind verloren, warum muß es nicht markiert werden? Ganz einfach: Wir markieren
einen Knoten genau dann, wenn er
1. irgendwann in der Wurzelliste stand
2. später als Kind eines anderen Knotens verschmolzen wurde
3. dann ein Kind verloren hat
5 erfüllt die 2. Bedingung nicht. An und für sich wären wir hier nun fertig. Es stellt
sich jedoch noch die Frage, wofür dieses Markieren überhaupt gut ist: Es soll verhindern, daß
ein Knoten mehr als ein Kind verliert, da wir dann die Laufzeitschranken nicht mehr halten
können. Wir sagen nun: Ein Knoten wird genau dann abgeschnitten und in die Wurzelliste
gesetzt, wenn er Punkte 1. bis 3. erfüllt hat und
4. zuletzt ein zweites Kind verloren hat
oder mit anderen Worten: Verliert ein bereits markierter Knoten ein Kind, wird er von
seinem Elter abgeschnitten und in die Wurzelliste gesetzt. Wir veranschaulichen dies mit
einem weiteren decrease key( 10, 2).
7. Wie immer wird erst der Schlüssel überschrieben, anschließend der Baum mit Wurzel 2 in
die Wurzelliste eingetragen. Jedoch war 7 markiert, und muß nun ebenfalls in der Wurzelliste
landen. Außerdem wird die Markierung aufgehoben.
5
15
min
6
3
7
9
12
2
11
min
2
6
5
11
15
9
7
3
12
min
7
2
6
5
3
11
15
9
12
Dieser Heap wird bei der nächsten erase min()-Operation wieder verschmolzen. Das wär’s.
Aufgabe 4 Dieser Lösungsvorschlag soll nicht in erster Linie zeigen, wie man eine solche Aufgabe
löst. Es geht mir hier darum eine Gefahr bei der vollständigen Induktion aufzuzeigen, die einem
schnell zum Verhängnis wird, wenn man nicht sorgfältig arbeitet.
Behauptung Es sei fn die n-te Fibonacci-Zahl und φ =
√
1+ 5
.
2
∀n ∈ N, n ≥ 2 : fn ≥ φn−2
Beweis durch vollständige Induktion über n.
Sei n = 2
fn = f0 + f1 = 1 = φ0 = φn−2
Sei n > 2 und die Aussage für n − 1 schon bewiesen.
fn = fn−1 + fn−2 ≥ φn−3 + φn−4
Induktionsannahme
√
3+ 5
= φ (φ + 1) = φ
·φ ·
√ 2
4
3+ 5
√
= φn−2 ·
·
2
1+2 5+5
n−2
= φ
n−4
n−2
−2
Da drängt sich einem die Frage auf, warum wir nicht gleich die stärkere Aussage fn = φn−2
zeigen. Der Beweis wäre doch der gleiche bis auf die Induktionsannahme?
Aber wenn wir mal
√
n = 3 einsetzen bekommen wir heraus f3 = 2 aber φ1 = 3+2 5 . Wo liegt also der Fehler? Ganz
einfach. Wir haben den Induktionsanfang nur für n = 2 gemacht, aber im Induktionsschritt
verwendet, dass die Induktionsannahme auch für n − 2 gilt. Deshalb ist unser Beweis im Grunde
nichts wert, da wir zwei Induktionsanfänge benötigen. Für n = 3 gilt nur fn ≥ φn−2 . Also ist der
Induktionsschritt für n > 3 durchzuführen.
Aufgabe 5 Wir zeigen zunächst, daß die angegebene Eigenschaft invariant unter den Ablöseund Verschmelz-Operationen sind.
• Ablöse-Operation: Die Ablöse-Operation wird durchgeführt, wenn ein markierter Knoten ein
weiteres Kind verliert. Der markierte Knoten wird dann (falls er nicht ein Wurzelknoten ist)
mitsamt seinem Unterbaum aus dem entsprechenden Baum gelöscht und in die Wurzelliste
eingefügt.
Wir werden nachher (bei der Betrachtung der Operation decrease_key) sehen, daß einem
markierter Knoten, der ein zweites Kind verloren hat, 4REs zur Verfügung stehen. Eine
davon wird für das Löschen verbraucht. Eine andere behält der Knoten, da er als Wurzelknoten in der Wurzelliste 1RE benötigt. Die restlichen 2REs werden dem Vaterknoten
übergeben.
Die Invariante ist damit wieder erfüllt: War der Vaterknoten noch nicht markiert, dann
wird er jetzt markiert und besitzt 2RE’s. War er bereits markiert und verliert jetzt ein
zweites Kind, so hat er jetzt 4RE’s zur Verfügung, so daß wieder eine Ablöse-Operation
durchgeführt werden kann.
• Verschmelze-Operation: Die Verschmelze-Operation setzt einen Knoten aus der Wurzelliste
mitsamt seinem Unterbaum in die Kinderliste eines anderen Knotens aus der Wurzelliste.
Diese Operation kostet 1RE, die von dem Knoten, der aus der Wurzelliste entfernt wird,
bezahlt werden kann. Die Invariante bleibt erhalten.
Jetzt betrachten wir die einzelnen Operationen minimum, insert, decrease_key und erase_min.
• minimum: Hier wird an der Struktur nichts geändert, es wird einfach der Zeiger auf das
Minimum (in der Wurzelliste) zurückgegeben. Eine RE wird der Operation minimum zur
Ausführung mitgegeben, weitere RE’s werden nicht gebraucht.
• insert: Hier wird ein neuer Knoten (als Wurzel eines Fibonacci Baums) in die Wurzelliste
eingetragen. Die anderen Knoten werden nicht betrachtet. Der neue Knoten soll 1RE erhalten. Für die Operation insert muß man also 2RE bezahlen: Eine für die Ausführung und
eine für den neuen Knoten.
• decrease_key: Für diese Operation bezahlen wir 3REs. Dabei wird eine für die eigentliche
Operation benötigt. Bei dieser Operation wird der Schlüssel erniedrigt und der Knoten dann
als neue Wurzel in die Wurzelliste gesetzt.
Die anderen beiden REs werden dem Vaterknoten übergeben. Falls dieser noch kein Kind
verloren hat, wird er jetzt markiert und besitzt die geforderten 2REs. War er bereits markiert, so besitzt er jetzt 4REs, womit man die Ablöseoperationen durchführen kann.
Man sieht also, daß die drei Operationen minimum, insert und decrease_key nur konstant viele
REs benötigen, um die Invariante aufrecht zu erhalten. Die Laufzeit für diese Operationen ist also
amortisiert konstant.
• erase_min: Bei dieser Operation wird das Minimum gelöscht und die Kinder des Minimums als neue Wurzeln in die Wurzelliste eingetragen. Dann werden Wurzeln mit gleicher
Kinderzahl verschmolzen.
Wir haben gesehen, daß die Verschmelze-Operationen aufgrund der Invariante ohne zusätzliche REs bezahlt werden können. erase_min muß also ’nur’ für das Löschen des Minimums,
das Eintragen der Kinder in die Wurzelliste und das Aufrechterhalten der Invariante bezahlen.
Das Löschen des Minimums kostet 1RE (die auch von dem Minimum selbst bezahlt werden
kann). Das Eintragen der Kinder in die Wurzelliste mit Aufrechterhaltung der Invariante
kostet 2RE pro Kind: 1RE für das Eintragen und 1RE für die neue Wurzel in der Wurzelliste.
In der Vorlesung wurde gezeigt, daß die maximale Kinderzahl einer Wurzel in einem Fibonacci Heap O(log n) ist. Damit muß man für erase_min 2O(log n) = O(log n)RE bezahlen.
Herunterladen