Algorithmen und Datenstrukturen SS09 - Foliensatz 6

Werbung
Algorithmen und Datenstrukturen SS09
Foliensatz 6
Michael Brinkmeier
Technische Universität Ilmenau
Institut für Theoretische Informatik
Sommersemester 2009
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 1 / 35
Datenstrukturen für dynamische Mengen
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 2 / 35
Datenstrukturen für dynamische endliche Mengen
Ziel
Entwurf von Datenstrukturen für die Repräsentation und Verwaltung von
dynamischen endlichen Teilmengen einer gegebenen Grundmenge D.
Speichere eine veränderliche endliche Menge S ⊆ D mit den folgenden
Operationen:
empty: Setze S = ∅.
member: Teste, ob x ∈ S für ein gegebenes x ∈ D.
insert: Füge ein gegebenes x ∈ D zu S hinzu.
delete: Entferne ein gegebenes x ∈ D aus S.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 3 / 35
Die Signatur für Sets
Die Signatur für Sets
Sorten:
Elements
Sets
Boolean
Operationen:
empty: () → Sets
insert: Sets × Elements → Sets
delete: Sets × Elements → Sets
member: Sets × Elements → Boolean
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 4 / 35
Die Modellalgebra für Sets
Die Modellalgebra drängt sich förmlich auf.
Die Modellalgebra für Sets
Elements =
ˆ
Sets =
ˆ
Boolean =
ˆ
einer Menge D
P <∞ (D) := {S ⊆ D | S ist endlich}
{true, false}
empty() := ∅
insert(S, x) := S ∪ {x}
delete(S, x) := S \ {x}
(
true
member(S, x) :=
false
falls x ∈ S
falls x ∈
6 S
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 5 / 35
Implementierung mit Wiederholung
Die wohl einfachste Implementierung von Sets verwendet eine einfach
verkettete Liste oder ein Array und erlaubt Wiederholungen, d.h. ein Element
kann mehrere Male in der Liste bzw. dem Array auftreten.
2
5
2
1
h
2 5 2 1 7 11 7 ∗
1
7
···
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 6 / 35
∗
11
pegel = h
7
Implementierung mit Wiederholung
Operation
Laufzeit
empty
Erzeuge leere Liste/Array
O(1)
member(S, x)
Suche das erste Auftreten von x
O(h)
(h = Länge der Liste, bzw. pegel = h)
insert(S, x)
Füge x vorne in die Liste ein
Füge x in A[pegel + 1] in das Array ein
O(1)
delete(S, x)
Suche und entferne alle Vorkommen von
x in der Liste/dem Array
O(h)
Wie vermeidet man bei delete Lücken im Array?
Wenn A[j] gelöscht werden soll, dann überschreibe es mit A[pegel] und
senke pegel um 1.
Achtung: Nach dem Kopieren, muss der neue Wert überprüft werden!
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 7 / 35
Löschen im Array
Löschen aller Vorkommen von x im Array
i = 1;
while i ≤ pegel do
if A[i] == x then
// Lösche durch Überschreiben
A[i] = A[pegel];
pegel = pegel − 1;
end
else
// Gehe zur nächsten Position
i = i + 1;
end
end
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 8 / 35
Implementierung mit Wiederholung
Die Implementierung mit Wiederholung hat einen gravierenden Nachteil:
Wenn oft schon vorhandene Elemente eingefügt werden, dann ist die Liste
deutlich größer als |S|. Dadurch ist der Zeit- und Platzaufwand unnötig hoch.
Konsequenz
Wir sollten versuchen die Wiederholungen zu vermeiden.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 9 / 35
Einfache Implementierung ohne Wiederholung
Wir können die Wiederholungen vermeiden, indem wir insert abändern:
insert(S, x): if member(S, x) == false then füge x ein
delete muß nicht mehr bis zum Ende der Liste/des Arrays laufen, sondern
nur bis zum ersten Vorkommen.
Nachteil: Der Zeitaufwand von member, insert und delete ist jeweils
O(|S|), denn in allen drei Fällen ist eine lineare Suche notwendig.
Vorteil: Der Platz zur Speicherung von S ist Θ(|S|).
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 10 / 35
Verbesserte Implementierung ohne Wiederholung
Idee
Besitzt D eine totale Ordnung ist eine effizientere Speicherung möglich,
indem man die Elemente in der Liste/dem Array aufsteigend sortiert.
Die Menge {1, 2, 3, 5, 7, 8, 11} kann dan folgendermaßen repräsentiert
werden:
1
2
3
5
1
h
1 2 3 5 7 8 11 ∗
7
···
∗
8
11
pegel = h
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 11 / 35
Aufsteigend sortierte Listen
Vorteil
Die lineare Suche kann beim ersten Eintrag y ≥ x abgebrochen werden.
Seien a1 < a2 < · · · < an die Elemente von S und x ∈ S das zu suchende
Element in S.
Falls P(x = ai ) = n1 , ergibt sich die erwartete Anzahl der Vergleiche als:
(1 + 2 + · · · + n) ·
1
n+1
.
=
n
2
Ist x nicht in S, aber ist x mit gleicher Wahrscheinlichkeit in jeder Lücke, d.h.
1
P(x < a1 ) = P(x > an ) = P(ai −1 < x < ai ) =
n+1
für jedes 2 ≤ i ≤ n, dann ist die erwartete Anzahl der Vergleiche
(1 + 2 + · · · + n + n) ·
1
n(n + 3)
n
=
< + 1.
n+1
2(n + 1)
2
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 12 / 35
Aufsteigend sortierte Listen
Nachteil
Bei Einfügen muss das neue Element an der richtigen Stelle eingefügt
werden.
Das erfordert wieder eine lineare Suche.
Konsequenz
Die Worst-Case Laufzeiten ändern sich nicht, aber man erhält eine geringere
erwartete Laufzeit.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 13 / 35
Aufsteigend sortierte Arrays
Nachteil
insert und delete benötigen das Verschieben vieler Elemente, um die
Ordnung zu erhalten.
Damit ist der Zeitaufwand Θ(|S|), denn die Elemente, mit denen nicht
verglichen wird, müssen verschoben werden.
Vorteil
member kann binäre Suche verwenden (AuP).
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 14 / 35
Binäre Suche
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 15 / 35
Die Aufgabe
Problem: Lookup
Gegeben:
Eine total geordnete Menge (D, ≤)
Ein schwach geordnetes Array A[0 . . . n] über D,
d.h.
A[0] ≤ A[1] ≤ · · · ≤ A[n]
zwei Indices 0 ≤ a, b ≤ n
ein x ∈ D
Gesucht:
(
true
falls x ∈ A[a . . . b]
false falls x 6∈ A[a . . . b]
und ia,b (x) = min (b + 1, {j | a ≤ j ≤ b und A[j] ≥ x}).
ιa,b (x) =
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 16 / 35
Die Aufgabe
ia,b (x) = min (b + 1, {j | a ≤ j ≤ b und A[j] ≥ x})
ist der kleinste Index eines Elementes y ≥ x in A[a . . . b], oder b + 1, falls
alle Einträge von A[a . . . b] echt kleiner als x sind.
1 2 3 4 5 6 7 8 9 10 11
A: 4 4 7 7 7 9 9 10 12 15 15
i1,11 (7) = 3
erstes Vorkommen
i1,11 (8) = 6 erstes Vorkommen eines größeren Elementes
i1,11 (20) = 12
Argument größer als alle Einträge
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 17 / 35
Die Idee
Falls b < a, ist A[a . . . b] leer. Damit ist x nicht in dem Teilarray und
(ιa,b (x), ia,b (x)) = (false, b + 1).
Falls a = b, besteht A[a . . . b] aus genau einem Element, d.h. x ist
genau dann in A[a . . . b], falls A[a] = x. Somit gilt


falls A[a] = x
(true, a)
(ιa,b (x), ia,b (x)) = (false, a + 1) falls A[a] < x


(false, a)
falls A[a] > x
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 18 / 35
Die Idee
Falls a < b ist, sei m = ⌊(a + b)/2⌋ (die Mitte des Intervalls).
Falls x ≤ A[m], liegt i(x) zwischen a und m, sofern x in A enthalten ist.
a
A:
···
≤x
m
z
b
≥x
···
Ferner ist x genau dann nicht in A[a . . . b], wenn es nicht in A[a . . . m] ist.
Damit gilt
ia,b (x) = b + 1 ⇔ ia,m (x) = m + 1
Zusammengefasst ergibt sich
(
(true, ia,m (x))
(ιa,b (x), ia,b (x)) =
(false, b + 1)
falls ia,m (x) ≤ m
falls ia,m (x) = m + 1
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 19 / 35
Die Idee
Falls a < b ist, sei m = ⌊(a + b)/2⌋ (die Mitte des Intervalls).
Falls x > A[m] liegt i(x) zwischen m + 1 und b + 1.
a
m
b
z
···
···
<x
A:
<x
Ist x in A[a . . . b], so auch in A[m + 1 . . . b] und somit ia,b (m) = im+1,b (x).
Außerdem ist x genau dann nicht in A[a . . . b], wenn es nicht in
A[m + 1 . . . b] ist, d.h. auch in diesem Fall - und somit immer - gilt
(ιa,b (m), ia,b (m)) = (ιm+1,b (x), im+1,b (x))
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 20 / 35
Die rekursive binäre Suche
RecBinSearch(A, a, b, x) – Rekursive Binäre Suche
Eingabe: A[1 . . . n], a, b und x
Ausgabe: (ιa,b (x), ia,b (x))
if b < a then return (false, b + 1);
if b == a then
if A[a] == x then return (true, a);
if A[a] < x then return (false, a + 1);
if A[a] > x then return (false, a);
end
m = ⌊ a+b
2 ⌋;
if x ≤ A[m] then
(ι, i ) = RecBinSearch(A, a, m, x);
if i == m + 1 then return (false, b + 1);
else return (true, i );
else
return RecBinSearch(A, m + 1, b, x);
end
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 21 / 35
Korrektheit von RecBinSearch
Satz
Das Teilarray A[a . . . b] von A[1 . . . n] sei aufsteigend sortiert und es gelte
1 ≤ a ≤ n + 1 und 0 ≤ b ≤ n. Dann gilt:
Der Aufruf RecBinSearch(A, a, b, x) liefert (ιa,b (x), ia,b (x)) als Resultat.
Beweis
Die Aussage ist korrekt, falls a > b (erste Zeile).
Für b ≥ a beweisen wir die Aussage per Induktion über i = b − a + 1.
Induktionsanfang: Falls i = 1 ist b = a. Damit liefern die Zeilen 2-5 die
korrekte Ausgabe.
...
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 22 / 35
Korrektheit von RecBinSearch
Beweis (Fortsetzung)
Induktionsvoraussetzung: Die Behauptung gilt für alle Aufrufe
RecBinSearch(A, c, d, x) mit 1 ≤ d − c + 1 ≤ i.
Induktionsschritt: Sei i = b − a + 1 > 1. Dann gilt a < b. Somit führt der
Algorithmus die Zeilen 6-13 aus.
Es gilt
a+b
m=
2
und
a+b
m=
2
2a + 1
≥
= ⌊a⌋ = a
2
1
2b − 1
≤
= b−
=b−1<b
2
2
Damit sind beide Teilarrays – A[a . . . m] und A[m + 1 . . . b] – kürzer als das
Originalarray A[a . . . b] und der Algorithmus liefert nach IV das korrekte
Ergebnis.
...
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 23 / 35
Korrektheit von RecBinSearch
Beweis (Fortsetzung)
Fall 1: Falls x ≤ A[m] gilt, wie oben beobachtet
(
(true, ia,m (x)) falls ia,m (x) ≤ m
(ιa,b (x), ia,b (x)) =
(false, b + 1) falls ia,m (x) = m + 1
Das ist genau, was in den Zeilen 8-10 berechnet wird.
Fall 2: Falls x > A[m] gilt, wie oben beobachtet
(ιa,b (m), ia,b (m)) = (ιm+1,b (x), im+1,b (x))
was in Zeile 12 berechnet wird.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 24 / 35
Laufzeit von RecBinSearch
TRBS (i) seien die Worst-Case-Kosten für einen Aufruf von
RecBinSearch(A, a, b, x) mit b − a + 1 = i.
D.h. TRBS (i) ist die maximale Laufzeit über alle möglichen Arrays A und
Indexpaare (a, b).
Jeder Aufruf von RecBinSearch verursacht Kosten ≤ C , wenn man die von
ihm ausgelösten rekursiven Aufrufe nicht betrachtet.
Damit genügt es für jedes i die maximale Anzahl r (i) von Aufrufen inklusive
des ersten Aufrufes für b − a + 1 = i zu ermitteln.
Die Zeilen 1-5 liefern
r (0) = r (1) = 1
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 25 / 35
Laufzeit von RecBinSearch
Falls i ≥ 2, hat das Teilarray, auf dem RecBinSearch aufgerufen wird im
ersten Fall die Länge
a+b
b−a+2 ∗ b−a+1
∗
m−a+1=
−a+1 =
=
2
2
2
und im zweiten Fall
b−a ∗ b−a+1
a+b
=
−1+1 =
b − (m + 1) + 1 = b −
2
2
2
∗
∗
(= sind Übungsaufgaben)
Damit gilt
i
i
,r
r (i) ≤ 1 + max r
2
2
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 26 / 35
Laufzeit von RecBinSearch
Lemma
Für i ≥ 0 gilt
r (i) ≤ 1 + ⌈log i⌉.
Beweis
Wir beweisen die Behauptung per Induktion über i.
Induktionsanfang: Für i = 1 gilt r (1) = 1 = 1 + ⌈log 1⌉. Für i = 2 gilt
r (2) = 2 = 1 + ⌈log 2⌉.
Induktionsvoraussetzung: Für alle 1 ≤ j < i gilt
r (j) ≤ 1 + ⌈log j⌉.
...
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 27 / 35
Laufzeit von RecBinSearch
Beweis (Fortsetzung)
Induktionsschritt: Sei i ≥ 3. Nach Induktionsvoraussetzung gilt:
i
i
r (i) ≤ 1 + max r
,r
2
2
IV
i
i
≤ 1 + max 1 + log
, 1 + log
2
2
i
≤ 2 + log
.
2
Um weiterzumachen, benötigen wir eine Abschätzung für log 2i .
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 28 / 35
...
Laufzeit von RecBinSearch
Beweis (Fortsetzung)
Behauptung: Für i ≥ 3 gilt
i
≤ ⌈log i⌉ − 1.
log
2
Beweis der Behauptung: Da i ≥ 3 existiert ein j mit j ≥ 2, so dass
2j−1 < i = 2j−1 + l ≤ 2j
mit 1 ≤ l ≤ 2j−1 .
Damit gilt
j − 1 = log(2j−1 ) < ⌈log(2j−1 + 1)⌉ ≤ ⌈log i⌉ ≤ ⌈log 2j ⌉ = j.
...
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 29 / 35
Laufzeit von RecBinSearch
Beweis (Fortsetzung)
Weiter gilt
2j−1
l
i
j−2
j−2
=2
+
≤2
+
= 2 · 2j−2 = 2j−1 ,
2
2
2
und damit
i
log
≤ j − 1,
2
was die Behauptung beweist.
Nun können wir r (i) weiter abschätzen:
i
≤ 2 + ⌈log(i)⌉ − 1 = 1 + ⌈log(i)⌉ .
r (i) ≤ 2 + log
2
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 30 / 35
Laufzeit von RecBinSearch
Satz (Laufzeit von RecBinSearch)
Der Algorithmus RecBinSearch benötigt auf einen schwach geordneten
Array A[a . . . b] höchstens 1 + ⌈log(b − a + 1)⌉ rekursive Aufrufe und hat
Laufzeit O(log(b − a + 1)).
Korollar
Die Binäre Suche auf einem schwach geordneten Array A[1 . . . n] benötigt
höchstens Zeit O(log n).
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 31 / 35
Iterative Binäre Suche
Rekursionen sind im Allgemeinen technisch sehr aufwändig und damit sehr
teuer. Iterative Algorithmen sind häufig deutlich schneller. Daher versuchen
wir die rekursive Formulierung umzuschreiben.
Da die Rekursion nur die Ergebnisse nach außen“ weitergibt (tail recursion)
”
ist das nicht besonders schwer. Wir müssen nur in jeder Runde die
Parameter anpassen und die Abbruchbedingung korrekt implementieren.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 32 / 35
Iterative Binäre Suche
ItBinSearch(A, a, b, x) – Iterative Binäre Suche
Eingabe: A[1 . . . n], a, b und x;
Ausgabe: (ιa,b (x), ia,b (x));
if b < a then return (false, a + 1);
while a < b do
m = ⌊ a+b
⌋;
2
if x ≤ A[m] then b = m else a = m + 1;
end
if x > A[a] then return (false, a + 1);
if x < A[a] then return (false, aq);
if x == A[a] then return (true, a);
return (false, a);
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 33 / 35
Iterative Binäre Suche
Satz (Iterative Binäre Suche)
Der Algorithmus ItBinSearch liefert auf einem schwach geordneten Array
A[a . . . b] das korrekte Ergebnis.
Die Anzahl der Durchläufe durch den Rumpf der Schleife ist höchstens
⌈log(b − a + 1)⌉.
Die Laufzeit ist Θ(log n) im besten und im schlechtesten Fall.
Beweis.
Übungsaugabe!
Vorsicht! Die Argumentation verläuft etwas anders als bei der
Rekursion.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 34 / 35
Herunterladen