Handout - TU Ilmenau

Werbung
Algorithmen und Datenstrukturen SS09
Foliensatz 4
Michael Brinkmeier
Technische Universität Ilmenau
Institut für Theoretische Informatik
Sommersemester 2009
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 1 / 50
Grundlegende Datentypen
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 2 / 50
Atomare Datentypen
Die meisten höheren Programmiersprachen, kennen verschiedene atomare1
Datentypen. Für uns sind das im Wesentlichen die folgenden:
boolean – Wahrheitswert, zwei mögliche Werte: true/false, 0/1
integer – ganze Zahlen, Z
real – Reelle Zahlen, R
char – ASCII oder Unicode Zeichen
pointer – Zeiger auf Speicherbereiche
Auf realen Rechnern sind die Typen in der Regel mit Größen- oder
Genauigkeitsbesschränkungen versehen ( z.B. short/int/long,
float/double ).
1 griech.:
atomos – unteilbar
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 3 / 50
Zusammengesetzte Datentypen
Mittels verschiedener Operationen können weitere Datentypen konstruiert
werden. Zu ihnen gehören:
Verbünde – struct; Kombinationen von mehreren Daten
Felder oder Arrays – int[5]; Listen fester Länge, in denen jeder Eintrag
denselben Typ hat
Diese Arten von Datentypen und der Umgang mit ihnen wird als bekannt
vorausgesetzt.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 4 / 50
Grundlegende Datenstrukturen
Im Folgenden wollen wir uns mit einigen grundlegenden Datenstrukturen
beschäftigen:
Stacks
Listen
Queues
Dynamische Mengen
Bäumen
Hashtabellen
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 5 / 50
Die informale Beschreibung
Die natürliche Sprache ist mit Sicherheit die einfachste Methode einen
Datentyp zu beschreiben.
Beispiel (Stack in natürlicher Sprache)
Ein Stack (oder Stapel, Keller, LIFO – last-in-first-out) ist wie ein Stapel von
Gegenständen organisiert. Man kann immer nur
einen neuen Gegenstand auf den Stapel drauf legen (push),
den obersten Gegenstand vom Stapel betrachten (top)
oder den obersten Gegenstand entfernen (pop).
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 6 / 50
Die informale Beschreibung
Alternativ kann man versuchen die Datenstruktur grafisch oder mit Hilfe von
Beispielen zu beschreiben.
Beispiel (Beschreibung eines Stacks mittels einer Graphik)
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 7 / 50
Die informelle Beschreibung von Datentypen
Die informale Beschreibung erweist sich im Allgemeinen als zu unpräzise.
Eine Verbesserung stellt die informelle Beschreibung dar, bei der der
Datentyp mittels einer konkreten Implementation in einer beliebigen
Programmiersprache beschrieben wird.
Vorteile: einfach und direkt
Nachteile:
verschleiert die wesentliche Funktionalität
(eine Operation wird über den auszuführenden Quelltext definiert)
erschwert Korrektheitsbeweise
die gegebene Implementierung ist nicht universell nutzbar
(Programmiersprache ist fixiert)
fixiert bestimmte technische Entscheidungen
(Eine andere Implementation könnte für die gegebene Aufgabe deutlich
geeigneter sein)
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 8 / 50
Abstrakte Datentypen
Die Verwendung von Abstrakten Datentypen (ADT) erlaubt die Definition
des Datentyps von seiner Implementierung zu trennen.
Ein Abstrakter Datentyp besteht im Wesentlichen aus zwei Komponenten:
einer Signatur und
einer Semantik.
Für beide Teile spielt die Implementierung keine Rolle.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 9 / 50
Signaturen
Die Signatur eines ADT entspricht im Wesentlichen dem klassischen
C-Header, bzw. den Deklaratoren. Sie beschreibt, welche Operationen im
Zusammenhang mit dem ADT ausgeführt werden können.
Definition (Signatur)
Eine Signatur Σ = (S, O, domain, codomain) besteht aus
einer Menge S von Sorten, die im Zusammenhang mit der ADT
benötigt werden,
einer Menge O von Operationen,
zwei Abbildungen domain: O → Seq(S) und codomain: O → S wobei
Seq(S) = {(S1 , . . . , Sn ) | n ∈ N, S1 , . . . , Sn ∈ D}
die Menge aller endlichen Tupel über S ist.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 10 / 50
Signaturen
Ist Σ = (S, O, domain, codomain) eine Signatur, dann kann man eine
Operation op ∈ O als Abbildung von ihrem Definitionsbereich (Domain,
Domäne) domain(op) in ihren Wertebereich (Codomain) codomain(op)
interpretieren.
Notation
Operationen beschreibt man häufig in der Form
op : domain(op) → codomain(op)
Wenn domain(op) = (I1 , . . . , In ) und codomain(op) = O schreibt man auch
op : I1 × · · · × In → O.
Die Forderung, dass jede Operation genau einen Ausgabetyp hat, ist auf
technische Gründe zurück zu führen.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 11 / 50
Die Signatur eines Stacks
Beispiel (Die Signatur eines Stacks)
Sorten:
Elements
Stacks
Boolean
Operationen:
empty : () → Stacks
push : Stacks × Elements → Stacks
pop : Stacks → Stacks
top : Stacks → Elements
isempty : Stacks → Boolean
Bemerkung
Der Definitionsbereich () von ist explizit erlaubt. Man spricht dann von
Konstanten oder auch von Konstruktoren.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 12 / 50
Signaturen
Bemerkung
Wenn eine Operation den Inhalt eines Objektes verändert, muss das Objekt
das Ergebnis der Operation sein!
Bei den Signaturen handelt es sich um eine funktionale (applikative)
Herangehesweise.
Beispiel
push: Stacks × Elements → Stacks
pop : Stacks → Stacks
verändern beide den Stack.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 13 / 50
Algebren
Eine Signatur Σ = (S, O, domain, codomain) ist lediglich eine Hülle, die die
Syntax der Datenstruktur beschreibt. Es fehlt noch der Inhalt.
Definition (Algebra)
Sei Σ = (S, O, domain, codomain) eine Signatur. Eine Σ-Algebra
A = (T , F ) besteht aus
einer Familie T = {TS | S ∈ S} von Mengen
und einer
Familie
F = fop | op ∈ O und f : Tdomain(op) → Tcodomain(op) von
Abbildungen.
Dabei ist T(S1 ,...,Sn ) = TS1 × · · · × TSn .
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 14 / 50
Algebren
Anders gesagt:
Eine Σ-Algebra A = (T , F ) mit Σ = (S, T , domain, codomain)
weist jeder Sorte S ∈ S eine Menge TS von Objekten dieser Sorte zu
und jeder Operation op : I1 × · · · × In → O eine Abbildung
fop : TI1 × · · · × TIn → TO .
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 15 / 50
Eine Algebra für Stacks
Beispiel (Eine Algebra für Stacks)
Elements = (nicht-leere) Menge D
Boolean = {true, false}
Stacks = Seq(D)
Das leere Tupel () stellt den leeren Stack dar.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 16 / 50
Eine Algebra für Stacks
Beispiel (Eine Algebra für Stacks – Fortsetzung)
empty() := ()
push((a1 , . . . , an ), x) := (a1 , . . . , an , x)
(
⊥ steht für einen Feh((a1 , . . . , an−1 ), an ) falls n ≥ 1
ler oder einen undefipop((a1 , . . . , an )) :=
⊥
fallsWert.
n=0
nierten
(
(a1 , . . . , an−1 ) falls n ≥ 1
pop((a1 , . . . , an )) :=
⊥
falls n = 0
(
an falls n ≥ 1
top((a1 , . . . , an )) :=
⊥
falls n = 0
(
false falls n ≥ 1
isempty((a1 , . . . , an )) :=
true
falls n = 0
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 17 / 50
Axiome
Um die Semantik einer Signatur festzulegen genügt es in der Regel nicht eine
Algebra anzugeben. Häufig müssen zusätzlich bestimmte Gleichungen oder
Axiome erfüllt werden.
Beispiel (Die Axiomatik eines Stacks)
∀s ∈ Stacks, ∀x ∈ Elements:
top(push(s, x)) =
pop(push(s, x)) =
x
s
isempty(empty()) = true
isempty(push(s, x)) =false
top(empty()) =
⊥
pop(empty()) =
⊥
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 18 / 50
Die Algebra und die Axiome
Beispiel (Die Axiome und unsere Stack-Algebra)
top (push((a1 , . . . , an ), x)) = top ((a1 , . . . , an , x)) = x
pop (push((a1 , . . . , an ), x)) = pop ((a1 , . . . , an , x)) = (a1 , . . . , an )
isempty(empty()) = isempty(()) = true
isempty(push((a1 , . . . , an ), x)) = isempty((a1 , . . . , an , x)) = false
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 19 / 50
Die Modellalgebra
Hat man eine Signatur Σ und dazugehörige Axiome gegeben, so spricht man
von einer algebraischen Spezifikation.
Aus einer solchen algebraischen Spezifikation läßt sich eine Modellalgebra
(oder auch mathematisches Modell) konstruieren (Stichwort:
Quotiententermalgebra, AuP). Diese Modellalgebra hat einige sehr wichtige
Eigenschaften, die bei dem Beweis der Korrekheit einer Implementierung
helfen können.
Unter Umständen lässt sich diese Modellalgebra auf elegante und einfach
Weise beschreiben.
Beispiel
Die beschriebene Algebra für Stacks ist eine Modellalgebra für die gegebene
algebraische Spezifikation.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 20 / 50
Algebraische Spezifikation eines ADT
Vorteile:
Universelle und vollständige Beschreibung
Die axiomatische Darstellung ermöglicht automatisiere Beweise von
Eigenschaften der Datenstruktur
Nachteile:
Das Finden eines geeigneten und vollständigen Axiomensystems ist in
der Regel nicht einfach.
Umsetzung in eine Implementation erfordert in der Regel einen weiteren
Zwischenschritt über die Modellalgebra. Diese explizit zu beschreiben ist
in der Regel ebenfalls sehr schwer.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 21 / 50
Implementierungen von Abstrakten Datentypen
Das Konzept von Abstrakten Datentypen lässt sich in gewisser Weise in der
Objektorientierung wiederfinden.
Im Wesentlichen entspricht die Signatur der Definition der Klassen (z.B. in
C++ und Java) und Interfaces. Abstrakte Klassen entsprechen direkt einer
Signatur.
Eine Implementierung entspricht einer Algebra.
Sogar die abstrakte Definition von Signaturen mit nicht näher spezifizierten
Sorten – wie z.B. die Sorte Elements eines Stacks – findet sich in modernen
objektorientierten Sprachen in Templates wieder.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 22 / 50
Implementierungen eines Stacks
Zur Realisierung eines Stacks über eine Menge D von Elementen, ergeben
sich im wesentlichen zwei mögliche Implementierungen:
Die Listenimplementierung
Die Arrayimplementierung
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 23 / 50
Die Listenimplementierung von Stacks
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 24 / 50
Die Listenimplementierung eines Stacks
Nullzeiger
Listen haben Sie schon in AuP kennengelernt.
S:
a1
a2
an
···
S:
emptyLI : Erzeuge eine leere Liste.
pushLI (s, x): Füge am Anfang der Liste einen neues Element ein.
S:
x
a1
a2
an
···
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 25 / 50
Die Listenimplementierung eines Stacks
topLI (s): Gebe den Inhalt des ersten Listenknotens zurück, falls
vorhanden
a1
S:
a2
an
···
popLI (s): Entferne das erste Element der Liste.
S:
a1
a2
a3
···
an
isemptyLI (s): Gibt true zurück, falls die Liste leer ist, sonst false.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 26 / 50
Korrektheit der Listenimplementierung
Da eine Modellalgebra für Stacks bekannt ist, kann die Korrektheit einer
Implementierung auf einfache Art bewiesen werden. Dabei nutzt man das
folgende Resultat:
Sei A = (T , F ) eine Modellalgebra von Σ = (S, O, domain, codomain) und
I = (T I , F I ) eine Implementierung (interpretiert als Algebra).
I
die in I.
TS und fop seien die Mengen und Abbildungen in A und TSI und fop
I ist eine korrekte Implementierung von Σ oder A, wenn
für jede Sorte S ∈ S eine Bijektion ϕS : TS → TSI existiert
und für jede Operation op : I1 × · · · × In → O Folgendes gilt:
I
(ϕI1 (x1 ), . . . , ϕIn (xn )) .
ϕO fop (x1 , . . . , xn ) = fop
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 27 / 50
Korrektheit der Listenimplementierung
Anders formuliert:
Wir benötigen für jede Sorte S eine Bijektion ϕS : TS → TSI zwischen den
Elementen der Sorte in der Modellalgebra und den Objekten der Sorte in der
Implementation.
Weiter müssen diese Bijektionen mit den Operationen kompatibel sein, d.h.
im folgenden Diagramm ist es egal welchen der beiden Wege wir von der
linken oberen Ecke zur rechten unteren Ecke nehmen. Beide führen zum
selben Ergebnis.
fop
I1 × · · · × In
O
ϕO
ϕI1 × · · · × ϕIn
I1I × · · · × InI
I
fop
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 28 / 50
OI
Korrektheit der Listenimplementierung
Wie sieht nun die Bijektion zwischen Seq(D) und den Listen aus?
Notation
a1
S:
a2
···
an
Das oben dargestellte Array notieren wir zur Abkürzung in der Form
[a1 → a2 → · · · → an ]
Damit definieren wir
ϕ((a1 , . . . , an )) := [an → an−1 → · · · → a2 → a1 ] .
(Beachten Sie, dass die Reihenfolge umgekehrt wurde).
Für Elements und Boolean gehen wir einfach von den Identitäten aus.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 29 / 50
Korrektheit der Listenimplementierung
((a1 , . . . , an ), x)
(a1 , . . . , an , x)
push
Seq(D) × D
Seq(D)
ϕ
List < D > × D
ϕ
pushLI
([an → · · · → a1 ], x)
[x → an → · · · → a1 ]
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
List < D >
Seite 30 / 50
Korrektheit der Listenimplementierung
(a1 , . . . , an )
(a1 , . . . , an−1 )
pop
Seq(D)
Seq(D)
ϕ
ϕ
List < D >
popLI
[an → · · · → a1 ]
List < D >
[an−1 → · · · → a1 ]
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 31 / 50
Korrektheit der Listenimplementierung
Auf ähnliche Weise kann man die übrigen Operationen auf Kompatibilität
mit ϕ überprüfen.
Satz
Die beschriebene Listenimplementierung ist eine korrekte Implementierung
des abstrakten Datentyps Stack.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 32 / 50
Zeitaufwand der Listenimplementierung
Behauptung
Jede Operation der Listenimplementierung hat Kosten O(1).
Die Behauptung ist offensichlich korrekt.
Allerdings erfordert push das Anlegen eines neuen Listeneintrages.
Diese Operation (malloc in C, new in C++ und Java) ist auf realen
Maschinen in der Regel deutlich teurer als der Zugriff auf einen Listeneintrag.
Noch ein Problem
Auf realen Maschinen kann push zu einem Speicherüberlauf führen. Im
Beweis sind wir stillschweigend von einem unendlich großem Speicher
ausgegangen. Die Korrektheit gilt also nur bis auf Speicherüberläufe.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 33 / 50
Die Arrayimplementierung von Stacks
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 34 / 50
Die Arrayimplementierung von Stacks
In der Arrayimplementierung besteht ein Stack s aus zwei Komponenten:
einem Array A[1 . . . m] der festen Größe m und
einer Integervariablen p, genannt Pegel.
p: n
s:
1
A : a1 a2
···
n
an ∗
···
m
∗ ∗
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 35 / 50
Die Arrayimplementierung von Stacks
empty(): A = new Elements[1 . . . m]
p = 0.
push(s, x): if p == m then return ⊥
else p = p + 1 ; A[p] = x
pop(s): if p == 0 then return ⊥
else p = p − 1 ; return A[p + 1]
top(s): if p == 0 then return ⊥
else return A[p]
isempty(s): if p == 0 then return true else return false
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 36 / 50
Korrektheit der Arrayimplementierung
Diesmal gehen wir einen anderen Weg für den Korrektheitsbeweis.
Wir betrachten eine Folge
(op0 = empty, op1 , . . . , opN )
von Operationen auf demselben Stack mit op1 , . . . , opN 6= empty.
Im mathematischen Modell erzeugt diese Operationsfolge eine Folge
(s0 , s1 , . . . , sN )
von Tupeln mit s0 = () und eine Folge
(z0 , z1 , . . . , zN )
von Ausgaben aus Elements ∪ {true, false} ∪ {−, ⊥}, wobei − die
Ausgabe von empty und push ist und ⊥, falls ein Fehler auftritt (pop oder
top auf leeren Stack). pop und top liefern das entsprechende Element als
Ausgabe.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 37 / 50
Korrektheit der Arrayimplementierung
Behauptung
Die Arrayimplementierung erzeugt genau dieselbe Ausgabefolge wie das
mathematische Modell, solange weder im mathematischen Modell ein Fehler
auftritt (top ode pop auf leeren Stack), noch die Höhe des Stacks si die
Arraygröße m überschreitet.
Beweis
Man beweist per Induktion über i = 0, 1, . . . , N, dass nach Ausführen von
opi die folgende Invariante gilt, sofern kein Fehler aufgetreten ist:
p enthält die Länge ni von si und
si = (A[1], . . . , A[ni ]).
D.h. das Array A[1 . . . ni ] stellt genau si dar.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 38 / 50
...
Korrektheit der Arrayimplementierung
Beweis (Fortsetzung)
Induktionsanfang (i = 0) : Nach op0 = empty() hat p den Inhalt 0 und
somit ist das Array A[1 . . . p] leer, also gleich s0 = ().
Induktionsvoraussetzung: Die Behauptung gilt für i − 1.
Induktionsschritt: Wir müssen verschiedene Fälle unterscheiden:
Falls si−1 =⊥ – d.h. im mathematischen Modell ist vorher ein Fehler
aufgetreten – ist nichts zu zeigen, da dieser Fall ausgeschlossen wurde.
Falls in den Schritten 1, . . . , i − 1 in der Implementierung ein Fehler
aufgetreten ist, ist ebenfalls nichts zu zeigen.
Wir können also si −1 = (a1 , . . . , ani −1 ) annehmen und p hat den Inhalt ni −1
und aj = A[j] für 1 ≤ j ≤ ni −1 .
...
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 39 / 50
Korrektheit der Arrayimplementierung
Beweis (Fortsetzung)
Falls opi = push(s, x), ist si = (a1 . . . , ani −1 , x). Wenn ni −1 < m, wird
von der Prozedur push(s, x) das Objekt x an die Stelle A[ni −1 + 1]
gesetzt und p auf den Wert ni −1 + 1 erhöht. Das impliziert die
Induktionsbehauptung in diesem Fall.
Wenn ni −1 = m ist, tritt in der Implementierung ein Fehler auf und die
Behauptung ist trivialerweise erfüllt.
Die anderen Operationen können analog behandelt werden (Übungsaufgabe).
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 40 / 50
Die Laufzeit der Arrayimplementierung
Behauptung
Bei der Arrayimplementierung hat jede Operation die Kosten O(1).
Die Behauptung ist wahr, falls bei empty() das Array lediglich angelegt,
nicht aber initialisiert wird. Im letzten Fall, wäre die Laufzeit für empty
gerade O(m).
Da der Inhalt in A[p + 1 . . . m] aber unerheblich ist, beeinflusst diese
Einschränkung die Korrektheit jedoch nicht.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 41 / 50
Arrayimplementierung ohne Platzprobleme
Frage
Ist es möglich, die Begrenzung auf ein Array fester Länge zu beheben?
Antwort
Ja! Mittels der Verdoppelungsstrategie!
Sobald während push(s, x) ein Überlauf eintritt – d.h. das m + 1-te Element
würde hinzugefügt werden – verdoppeln wir die Größe des Arrays.
Genauer:
Legen ein neues Array B der Größe 2m an.
Kopieren das alte Array A in die ersten m Felder des neuen Arrays B.
Gebe A frei.
Setze A = B, d.h. setze die Referenz um.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 42 / 50
Die Verdoppelungsstrategie
p: m
s:
1
A: a1 a2
1
B : a1 a2
···
m
am
···
m
am ∗
···
2m
∗ ∗
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 43 / 50
Kostenanalyse der Verdoppelungsstrategie
Frage
Wieviele Verdoppelungen können auftreten?
k sei die Anzahl der push-Opertationen in unserer Folge und j die Anzahl
der Verdoppelungen.
Damit enthält der Stack stets ≤ k Elemente.
Also gilt vor der letzten Verdoppelung
m0 · 2j−1 < k
denn sonst, müsste nicht verdoppelt werden, und somit
j − 1 < log
k
= log k − log m0 .
m0
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 44 / 50
Kostenanalyse der Verdoppelungsstrategie
Sei L = ⌈log k − log m0 ⌉.
Dann lassen sich die Gesamtkosten für die Verdoppelungen nach oben
abschätzen:
!
L
L−1
X
X
O m0 · 2i −1 = O m0 ·
2i
= O m0 · 2 L
i =1
i =0
= O m0 · 2log
k
m0
= O(k)
k
= O m0 ·
m0
Gesamtkosten mit k push-Operationen
N · O(1) + O(k) = O(N)
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 45 / 50
Kostenanalyse der Verdoppelungsstrategie
Satz
Wenn ein Stack mit Arrays und der Verdoppelungsstrategie implementiert
wird, sind die Gesamtkosten für N Operationen in O(N).
Der gesamte Platzbedarf ist O(k), wenn k die maximale je erreichte
Stackgröße ist.
Selbst wenn unbenutzte Arrays nie freigegeben werden, tritt ein
Speicherüberlauf erst dann ein, wenn die Zahl der Stackeinträge zu einem
bestimmten Zeitpunkt mehr als 14 des zur Verfügung stehenden Speichers
beansprucht (Übungsaufgabe).
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 46 / 50
Amortisierte Analyse
Eine Analyse der Kosten, wie wir sie gerade durchgeführt haben, nennt man
Amortisierte Analyse.
Idee hinter der amortisierten Analyse
Eine Datenstruktur wird häufig nicht für eine Operation, sondern für eine
Reihe von insgesamt N Operationen genutzt.
Betrachtet man die Kosten der einzelnen Operationen, so ergeben sich unter
Umständen sehr hohe Worst-Case Kosten, wie z.B. die Kosten von push bei
einem Stack mit Verdoppelungsstrategie.
Betrachtet man jedoch die gesamte Folge, so könnte es sein, das diese
relativ hohen Kosten nur in bestimmten Situationen eintreten (z.B. wenn
besonders viele billige push Operationen seit der letzten teuren durchgeführt
wurden). Dadurch ergeben sich insgesamt niedrigere Kosten, als bei der
Verwendung der Worst-Case Laufzeit.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 47 / 50
Amortisierte Analyse
Amortisierte Kosten
Benötigt man im schlechtesten Fall T (N) Zeit für N Operationen auf einer
(N)
.
Datenstruktur, so benötigt jede Operation die amortisierten Kosten T N
Wichtig!
Amortisierte Kosten müssen klar von Average-Case Kosten unterschieden
werden!
Bei letzteren geht eine Wahrscheinlichkeitsverteilung ein, und man
betrachtet die erwartete Laufzeit.
Bei den amortisiertern Kosten handelt es sich um eine tatsächliche obere
Schranke, d.h. N beliebige Operationen auf der Datenstruktur benötigen
höchstens T (N) Zeit. Die amortisierten Kosten für jede einzelne Operation
sind dann die entstandenen durchschnittlichen Kosten für jede Operation.
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 48 / 50
Vergleich von Listen- und Arrayimplementierung
Arrayimplementierung:
O(1) Kosten pro Operation, amortisiert – d.h. im Mittel über alle
Operationen.
Sequentieller, indexbasierter Zugriff auf den Speicher (cachefreundlich)
Höchstens die Hälfte des allozierten Speichers ist ungenutzt
Listenimplementierung:
O(1) Kosten pro Operation im schlechtesten Fall
Hohe Allokationskosten, da jedes Listenelement einzeln angelegt wird
Zusätzlicher Platz für Zeiger benötigt
Algorithmen und Datenstrukturen SS09
M. Brinkmeier
TU Ilmenau
Seite 49 / 50
Herunterladen