Algorithmen und Datenstrukturen (ALP 3)

Werbung
Algorithmen und Datenstrukturen (ALP 3)
Simon Putzke
• 16. Oktober 2008
• 21. Oktober 2008
• 23. Oktober 2008
• 30. Oktober 2008
• 04. November 2008
• 06. November 2008
• 11. November 2008
• 13. November 2008
• 18. November 2008
• 20. November 2008
• 25. November 2008
• 27. November 2008
• 02. Dezember 2008
• 04. Dezember 2008
• 09. Dezember 2008
• 11. Dezember 2008
1
Inhaltsverzeichnis
1 Analyse von Algorithmen
1.1
1.2
1.3
4
Effizienz (insbesondere Laufzeit) von Algorithmen . . . . . . . . . . . . . . . . . . . . .
4
1.1.1
Experimentelle Analyse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
4
1.1.2
Theoretische Analyse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
1.1.3
Pseudo- Code . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
5
1.1.4
Registermaschine . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
1.1.5
Laufzeit von Algorithmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6
1.1.6
Asymptotisches Laufzeit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
8
1.1.7
Rekursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
9
1.1.8
Rekursion auf der RAM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
Datenstrukturen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.2.1
Beispiel Stapel (Stack) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.2.2
“Indirekte” Anwendung von Stapeln . . . . . . . . . . . . . . . . . . . . . . . . . 18
Dynamisierung Array- basierter Datenstrukturen . . . . . . . . . . . . . . . . . . . . . . 20
2 Datenabstraktion
24
2.1
Geheimnisprinzip . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
2.2
Abstrakte Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.2.1
Explizite Schnittstellenbeschreibung in Java . . . . . . . . . . . . . . . . . . . . . 26
3 ADT- Anwendungen, Implementierungen
3.1
28
Der ADT - Prioritätswarteschlange . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
3.1.1
Implementierung des ADT PWS (für U = N) . . . . . . . . . . . . . . . . . . . . 29
3.2
Implementieruung des Heap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.3
Erweiterung des ADT PWS : VPWS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
3.4
neue Implementierung als Binomialheaps . . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.5
Lokalisierung von Einträgen in Datenstrukturen . . . . . . . . . . . . . . . . . . . . . . . 39
3.6
Bäume . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3.6.1
Schnittstelle zum ADT Baum (Auswahl) . . . . . . . . . . . . . . . . . . . . . . . 41
3.6.2
weitere Anwendungen: Spielbäume . . . . . . . . . . . . . . . . . . . . . . . . . . 45
3.6.3
Breitensuche in Bäumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
2
3.6.4
3.7
3.8
Tiefensuche in Bäumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
ADT Wörterbuch (Dictionary) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
3.7.1
Implementierung des ADT Wörterbuch . . . . . . . . . . . . . . . . . . . . . . . 46
3.7.2
Implementierung durch Hashing . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.7.3
Hashcodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
3.7.4
Zufallsexperiment
3.7.5
Universelle Hashfunktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
ADT Geordnetes Wörterbuch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.8.1
Implementation des ADT G.W. . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3.8.2
Skip Liste: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
3
16. Oktober 2008
1
Analyse von Algorithmen
“Def.”: Ein Algorithmus ist eine Schritt-für-Schritt Anleitung zur Lösung eines Problems
• Speicherverbrauch kann durch geeigneten Algorithmus gering gehalten werden
• Zeitbedarf kann durch geeigneten Algorithmus gering gehalten werden
Frage nach
1. der Effizienz von Algorithmen
2. der Korrektheit von Algorithmen
1.1
Effizienz (insbesondere Laufzeit) von Algorithmen
• Algorithmus transformiert Eingabe in Ausgabe
• Die Laufzeit eines Algorithmus hängt von der Größe der Eingabe ab
(Funktion die einer Eingabe die Laufzeit des Algorithmus darauf zuordnet)
1.1.1
Experimentelle Analyse
oft gar nicht möglich, bzw. sinnvoll
• Implementierung
• Messung der Laufzeit auf vielen, “typischen” Eingaben
Problematisch!
4
1.1.2
Theoretische Analyse
• abstrakte Beschreibung des Algorithmus (Pseudocode)
• Charakterisierung der Laufzeit (als Funktion der Größe der Eingabe)
• dabei betrachten wir den schlechtesten Fall für alle Eingaben fester Länge (“worst case”- Analyse)
1.1.3
Pseudo- Code
• abstrakte Beschreibung eines Algorithmus “Programm”
• “strukturierter” (& detaillierter) als prosaische Beschreibung
• weniger detailliert als Java- Programm
Bsp.
ArrayMax (A, n)
Input: Feld A [1, ..., n] von n ganzen Zahlen
Output:
max
1≤i≤n
A [i]
currentMax ← A [1]
for i = 2 to n
if current Max < A [i] then currentMax = A [i]
return currentMax
Im Detail:
• Kontrollflussanweisungen (for, while, repeat, if then ... else, goto)
• Deklariationen
• Methodenaufrufe
• Wertrückgabe
• Ausdrücke (Zuweisungen, Tests, ...)
“Verboten”:
ArrayMax+ (A, n)
return
max
1≤i≤n
A [i]
• nicht verboten bei Test auf Korrektheit, aber bei Analyse
5
1.1.4
Registermaschine
• Rechenkern = arithm. Operationen (+, -, ·, /)
• Kontrollflussoperationen (JMP, bedingte Sprünge)
• all diese Operatoren können in einem Takt bearbeitet werden
– linear organisierter Speicher mit whalfreiem Zugriff
∗ jede Zelle kann eine ganze Zahl speichern
∗ auf jede Zelle kann in einem Takt (durch Angabe der Adresse) zugegriffen werden
∗ der Speicher ist unbegrenzt
Pseudo Code ist O.K. falls jede primtive Operation durch ≤ 10 RAM- Anweisungen ausgeführt werden
kann.
1.1.5
Laufzeit von Algorithmen
Def:
A Algorithmus (RAM- Programm)
I
Eingabe für A
TA (I)
= Anzahl der RAM Operationen die A auf Eingabe I macht “Rechenzeit von A uf I”
TA (n)
max
= max I,Groesse
von I=n TA (I) “worst- case Rechenzeit von A”
Laufzeit von A (Funktion N → N)
Beispiel (I) mit Addition:
Algorithm d o u b l e ( x )
6
Input x ∈ N
Output 2x
y <− x
y <− y + x
return y
(Pseudocode)
LOAD 0 (CPU <− x )
STORE 1 (CPU <− y )
LOAD 1 (CPU <− y )
ADD 0 (CPU <−CPU + x )
STORE 1 (CPU <− y )
LOAD 1 (CPU <− y )
RETURN RAM
Beispiel (II) ohne Addition:
Algorithm d o u b l e ( x )
Input x ∈ N
Output 2x
z <− x
y <− x
f o r i =1 t o z do
y <− y + 1
return y
(Pseudocode)
TA (x) − ”3 + 2x
Typischerweise analyisieren wir Algorithmen im Pseudocode (Laufzeit ≈Anzahl der Pseudocodezeilen)
Das ist zulässig, solange der verwendete Pseudo- Code die Eigenschaft hat, dass jede Code- Zeile durch
konstant viele RAM- Operationen realisiert wrrden können.
7
1.1.6
Asymptotisches Laufzeit
• “moderate” Änderungen des Maschinenmodells ändern die Laufzeit nur um einen konstanten
Faktor.
• die asymptotische Laufzeit ignoriert konstante Faktoren und Terme niederer Ordnung.
Bsp.:
TA (n) = 1, 75n3 + (0, 4 log2 n) = Θ(n3 )
Erinnerung (O- Notation):
DEF.:
f, g ∈ NN
f = O(g) ⇔ ∃c > 0 ∃n0 ∈ N ∀n ≥ n0 : f (n) ≤ c · g(n)
manchmal schreibt man auch für f = O(g) auch
1. f (n) = O(g(n))
2. f ∈ O(g)
BSP :
1. 12n − 4 ≤ 12n d.h. 12n − 4 = O(n)
2. n2 = O(n) gilt nicht!!
O-Notation HOWTO
1.)
!d
f (n) = i=0 ai ni mit ai ≥ 0
f (n) = O(nd )
2.)
Wir sagen ”2n = O(n)” statt ”2n = O(n2 )”
3.)
Wir sagen ”2n = O(n)” statt ”2n = O(3n − 6)”
4.)
Die Analogie O =
ˆ ≤, Ω=
ˆ ≥, θ =
ˆ =
klappt oft (aber Vorsicht: nicht immer)
5.)
8
nα = O(nβ ) ∀ α ≤ β
6.)
(log n)α = O(nβ ) ∀α, β > 0
Beispiel:
Algorithm PrefixAverage (X, n)
Input X[0], . . . , X[n − 1], n
Output A[0], . . . , A[n − 1] mit A[i] =
1
i+1
!
j≤i
A[j]
A<−l e e r e s Feld mit n Elementen //1−mal d u r c h l a u f e n
f o r i = 0 t o n−1 do
//n−mal d u r c h l a u f e n
sum <− 0
//n−mal d u r c h l a u f e n
f o r j = 0 t o i do
/ / ( i +1 mal d u r c h l a u f e n )
sum <− sum + X[ j ]
//im i −t e n D u r c h l a u f d e r ä u ß e r e n S c h l e i f e
A[ i ] <− ( sum ) / ( i +1)
//n−mal d u r c h l a u f e n
return A
//1−mal d u r c h l a u f e n
Gesamtkosten:
n−1
&
O(1) + O(n) + O(
(i + 1))
" #$ % " #$ %
i=0
1&7
2&6,3
"
#$
%
4&5
= O(1) + O(n) + O(n2 ) = O(n2 )
Algorithmus PrefixAverage2 (A,n)
In : s . o .
Out : s . o .
A <− l e e r e s Feld
sum <− 0
f o r i = 0 t o n−1 do
sum <− sum + X[ i ]
A[ i ]<− ( sum ) / ( i +1)
return A
//1−mal
//1−mal
//n−mal
//n−mal
//n−mal
//1−mal
Laufzeit O(n)
1.1.7
Rekursion
Beispiel:
Potenzieren x, n ∈ N
p(x, n) := xn = x
. . · x%
" · .#$
n−mal
9
Lösung 1: Iterartiv O(n)
'
x · p(x, n − 1)
Lösung 2: p(x, n) =
1
n>0
n=0
Algorithm Pow (x,n)
Input x, n ∈ N
Output xn
i f n = 0 then r e t u r n 1
r e t u r n x ∗ Pow ( x , n−1)
Laufzeit:
'
T (n) =
O(1)
T (n − 1) + O(1)
n=0
n>0
Lösung (n > 0) mit C > 0
T (n) ≤ T (n − 1) + C ≤ T (n − 2) + 2 · C ≤ T (n − 3) + 3 · C
per Induktion: T(n) ≤ T(n − k) + k · C
∀k ≤ 1 für k = n: T (n) ≤ T (0) + n · C
Beweis:
x · P ow(x, n − 1)
x∗Pow( x , n−1)=x∗x∗Pow( x , n−2)
=x∗x∗x∗Pow( x , n−3)
=x ∗ ( n−mal ) ∗ x∗Pow( x , 0 )
//Pow( x ,0)=1
Damit T (n) = O(n)
Lösung 3:


1

, , n --2
p(x, n) =
p x, 2

 , , n−1 --2
p x, 2
·x
n=0
n = 2k
n = 2k − 1
Algorithm PowFast(x,n)
i f n=0 r e t u r n 1
i f n=2k then
z <− PowFast ( x , 1 / 2 ) // n i c h t s o ! S y n t a k t i s c h a b e r k o r r e k t
return z∗z
i f n=2k+1 then
z <− PowFast ( x , ( n −1)/2)
r e t u r n z ∗ z ∗x
10
Laufzeitanalyse
Beschränken auf n = 2r für r ≥ 0
'
O(1)
n=1
, T (n) =
T n2 + O(1)
n>1
S(r) := T(2r )
'
S(r) =
wie in (2:)
O(1)
S(r − 1) + O(1)
r=1
r>0
S(r) = O(r) = T (2r )
also
T (n) = O(log n)
28.Oktober 2008
1.1.7.1 Rekursionsbäume
Bsp.: PowFast
Fibonacci Zahlen
Bsp: Fibonacci- Zahlen
11
F(0)=F(1)=1
F(n)=F(n-1)+F(n-2), n ≥ 2
Definition:
Rekursionbaum einer Prozedur A ist ein Baum TA , wobei
• jeder Aufruf der Prozedur entspricht einem Knoten
• Kinder von Knoten v sind die (rek.) Aufrufe von A die v tätigt (mit den jeweiligen Argumenten)
Die Rechenzeit von A auf x lässt sich bestimmen, indem man die in den Knoten des Berechnugsbam
von A auf x anfallende Arbeit aufsummiert.
Bsp: Mergesort
zum Sortieren von A[0], A[n + 1](n = 2k mit k ≥ 0)
Anm. d. A.:
Mergesort funktioniert wie Quicksort nach dem Teile- und- Herrsche- Prinzip.
Es zerlegt die Liste in immer kleinere Listen, bis die Liste nur noch aus einem Element besteht und
diese Teillisten werden dann sortiert Schritt für Schritt wieder zusammengeführt.
12
Analyse
(a) T (n) = 2 · T ( n2 ) + O(n)
per Induktion: T (n) = O(n · log n)
(b) Rekursionsaufruf für M.S. auf A[0], ..., A[n]
13
Gesamtkosten:
n
n
n
n
n
n
c · n + c( ) + c( ) + c( ) + c( ) + c( ) + c( ) + . . .
"#$%
2
2
4
4
4
4%
"
#$
%
"
#$
W urzel
Kinder d. W urzel
= c · (n +
n
2)
+
n
2)
+
( n4
Enkel der W urzel
+
n
4
+
n
4
+ n4 ) + . . .)
= c(n + n + n + n + . . .)
= c · n (1 + 1 + 1 + . . . + 1) = c · n· Höhe des Baumes = c · n · log n
"
#$
%
#Rekursionsstuf en
zu BSP (Fibonacci- Rekursion)
Laufzeit der direkten Rekursion
'
Θ(1)
T (n) =
T (n − 1) + T (n − 2) + Θ(1)
also
T (n) ≥
'
1
T (n − 1) + T (n − 2)
n≤1
n>1
n≤1
n>1
damit T (n) ≥ F (n) (per Induktion)
F(0)=F(1)=1
F(n)=F(n-1)+F(n-2), n > 1 (s.o.)
14
= F (n − 2) + F (n − 3) + F (n − 2)
≥ 2F (n − 2)
≥ 2(2F (n − 4))
≥ 2(2(2 · F (n − 6)))
≥ 2i · F (n − 2i) (per Induktion)
Damit (i = u2 )
√
n
F (n) = 2 2 = ( 2)n
Alternativen
• tabellieren der bereits berechneten F(1) für 0 ≤ i < n (dynamisches Programmieren)
Laufzeit O(n), Platzbedarf Ω(n)
• iterative Lösung
Laufzeit O(n), Platzbedarf Ω(1)
.
/ .
/ .
F (n)
F (n − 1) + F (n − 2)
1
•
=
=
F (n − 1)
F
(n
−
1)
1
.
/
F (n)
mit f$n :=
F (n − 1)
.
/
1 1
f$n =
·f$n−1
1 0
" #$ %
. M/
1
f$1 =
1
1
0
/.
F (n − 1)
F (n − 2)
/
Iteration:
Ind.
f$n = M · f$n−1 = M · M · f$n−2 = M · M · M · f$n−3 = M · . . . · M · f$n−i
"
#$
%
(i ≥ 1)
i−mal
= M i · f$n−i
i=n−1
=
M n−1 · f$1
Kann in O(log n) Zeit durch schnelle (Matrix-) Exponentiation berechnet werden.
1.1.8
Rekursion auf der RAM
Algorithm min ( )
x <− 5
subroutine1 (x)
15
Algorithm s u b r o u t i n e 1 ( i )
k <− i +1
subroutine2 (k)
Algorithm s u b r o u t i n e ( k )
y <− 6
• merken, welche Unterroutine gerade ausgeführt wird
• es gibt für jede Unterroutine eine Struktur in der ihr idealer Kontext (Variablenwerte, Rücksprungadresse) gespeichert wird
(vgl. Rekursionsaufruf in z.B. MMIX)
30. Oktober 2008
1.2
Datenstrukturen
Schema zur Organisation von Daten, so dass gewisse Operationen auf / Manipulationen von Daten
effektiv durchführbar sind.
1.2.1
Beispiel Stapel (Stack)
Manipulation von von Objekten erfolgt nach dem LIFO (last in- first out) Prinzip (hier: “Objekte”=int)
d.h. wir benötigen die Operationen
• einfügen eines neuen Elements (push)
• entfernen des zuletzt eingefügten Elements (pop)
• lesen des zuletzt eingefügten Elements (ohne es zu entfernen) (top)
• Test, ob der Stapel leer ist
(x# , S # ) = pop(push(x, S)) ⇒ x# = x und S # = S
wobei :
pop :
push :
S t a p e l ( i n t ) −> i n t x S t a p e l ( i n t )
i n t x S t a p e l ( i n t ) −> S t a p e l ( i n t )
Implementierung:
16
• int-Array F mit M Einträgen
• Zeiger top auf oberstes Element (int top=-1)
Melde F e h l e r , f a l l s Array F v o l l ( top = M−1)
− push ( x )
top <− top + 1
F [ top ] <− x
− pop ( )
r e t u r n F [ top −−]
F e h l e r s i g n a l i s i e r e n , f a l l s Stack l e e r i s t ( top = −1)
Java- Implementierung:
c l a s s i n t ArrayStack {
int F [ ] ;
i n t top ;
i n t M;
i n t ArrayStack ( i n t m) {
M = m;
top = −1;
F = new i n t M[ ] ;
}
i n t pop ( ) throws EmptyIntArrayStackEception {
i f ( top == −1) throw . . .
e l s e r e t u r n F [ top −−];
}
Analyse von Datenstrukturen
• Wie effizient sind die Operatoren auf der D.S.?
(in Abhängigkeit von der Anzahl der in der Struktur gespeicherten Objekte)
• Wie viel Speicherplatz benötigt die Struktur?
hier:
17
• Platzbedarf Θ(M )
• Θ(1) Zeit für push & pop
Probleme:
• maximale Größe fest
• Implementierungsspezifische Ausnahmebehandlung
Implemetierung mittels verketteten Listen
• einfach verkettete Liste
• Einfügen und Löschen am Anfang der einfach verketteten Liste ist einfach (Θ(1) Zeit )
1.2.2
“Indirekte” Anwendung von Stapeln
• als Hilfsstruktur für andere D.S.
• beim Entwurf von Algorithmen
Beispiel:
Gegeben
18
x2 wird von x3 verdeckt, x4 wird von x5 verdeckt. Wäre ein x6 größer als x5 und kleiner als x3, wäre
auch x5 verdeckt.
Span berechnen:
Feld X[0],...,X[1] von n Zahlen
Berechne für alle 0 ≤ i ≤ n − 1
S[i] = max. Anzahl von aufeinanderfolgenden Elementen unmittelbar vor X[i] die kleiner als X[i] sind.
Algorithm Span (X, n )
S <− Neues Feld mit n E i n t r ä g e n
|
f o r i = 0 t o n−1 do
|
S [ i ] <− 0
|
| f o r j = 0 t o i do
|
|
i f X[ i −j ] " k l e i n e r g l e i c h " X[ i ] do
|
|
S [ i ]++
|
|
e l s e break ;
i
n−1
&&
Θ(
1) = Θ(n2 )
i=0 j=0
04. November 2008
Besser mittels Hilfsfunktion
• arbeiten X von links nach rechts
• von rechts “sichtbare” Elemente im Stapel (von rechts nach links)
19
• am Index i
entferne alle Elemente von der Spitze des Stapels die ≤ X[i] sind und zähle span(X[i]) je um
eins hoch
lege X[i] auf Stapel
Algorithm span (X, n )
S <− n e u e s Feld von n Zahlen
A <− n e u e r l e e r e r S t a p e l
f o r i =0 t o n−1 do
w h i l e ( ! isEmpty (A)&&((X[ top (A)]) <= X[ i ] ) ) do // e n t f e r n e a l l e Elemente
pop (A)
// aus A d i e k l e i n e r a l s X[ i ] s i n d
i f isEmpty (A) S [ i ] <− i +1
e l s e S [ i ] <− i − top (A)
push ( i ,A)
≤ i wird gelöscht und i wird hinzugefügt.
Analyse:
n
≤n
≤ 2n
push − Operationen
pop − Operationen
isEmpty − Operationen
Stack mit einfach verketteten Listen
1.3
⇒ Θ(n) Gesamtlaufzeit.
Dynamisierung Array- basierter Datenstrukturen
Konkretes Beispiel :
Stapel
Ziel:
• Implemetierung von Stacks durch Arrays
20
• keine feste Größe
Idee: (nur push-Ops)
• Wir haben zu jedem Zeitpunkt ein Array in dem alle Elemente des Stapels abgespeichert sind.
• Wenn Platz in diesem Array nicht mehr ausreicht, legen wir ein neues Array doppelter Größer
an und kopieren das alte Feld in den Anfang des neuen Feldes.
Θ(1) Zeit für push-Op (solange noch Platz!)
Θ(M ) Zeit für push-Op die Umkopieren erfordert.
(typisch ist M’=2M, hier im Bild jedoch nicht.)
Probleme dabei:
Es gibt push- Operationen die Ω(#Elemente im Stapel) Zeit benötigen.
ABER:
Jede Folge von n push/pop- Operationen benötigt O(n) Zeit.
alternatives Argument via Bankiermethode.
Begründung:
1. Eine pop- Op benötigt nur Θ(1) Zeit. D.h. alle pop- Op’s in einer Folge von n push/pop- Op’s
benötigt O(n) Zeit.
21
2. Betrachte Folge von n push- Op’s.
Die k-te push- Operation benötigt Zeit Tk =
'
O(k)
O(1)
k = 2i
sonst
Gesamtzeit für n push- Operationen
n
&
Tk
= O(n) +
$log2 n%
&
T2i
i=0
k=0
= O(n) +
$log2 n%
&
O(2i )
i=0
= O(n) + O(n)
n
&
Tk
= O(n)
k=0
amortisierte Kosten von “O(1)”
Idee: (Wiederholung von oben)
• Jede elementare Operation verursacht gewisse Kosten
(z.B.: Kosten von pop: O(1) : 1$ ;
Kosten von von push (kein Umkopieren): O(1) : 1$ , (mit Umkopieren) O(Größe des Arrays : #Elemente
im Stack $))
Definition (amortisiert):
Gegeben sie eine Datenstruktur, für die eine Folge von n Operationen insgesamt T(n) Zeit benötigt.
Dann nennen wir
T (n)
n
die amortisierten Kosten pro Operation.
06. November 2008
• Eine Folge F von Operationen verursacht damit Gesamtkosten cF
(Ziel: zeige, dass cF = O(n) falls |F | = n)
• Wir werden zeigen: Falls wir jeder Operation 2$ “mitgeben”, können wir am Schluss die Kosten
cF begleichen.
22
Buchhaltervorschrift
• pop- Operation (bringt 2$ mit) :
bezahle die Kosten (1$) und lege 1$ auf Sparbuch (der DS)
• push- Operation (bringt 2$ mit) :
– kein Kopieren : bezahle Kosten (1$) und lege 1$ auf das DS Sparbuch
– mit Kopieren: bezahle Kosten (# Elemente im Stack) von meinen 2$ auf Sparbuch
Wir werden zeigen: das Sparbuch ist nie im Minus
1. die Kosten cF können bezahlt werden
2. es gilt cF = 2$ · |F |
• eine push- Op veranlasst, das im Fall mit M Einträgen verdoppelt wird, hat zur Folge, dass
danach mind. M Operationen ausgeführt werden können, die nur 1$ kosten.
• der nächste auffallende Verdopplung kostet 2M $.
Problem: Der Platzbedarf der DS hängt nicht von der Anzahl der Elemente (tatsächlich von der maximalen Stackgröße) ab, die in der DS gespeichert worden, sondern von der Anzahl der durchgeführten
Operationen.
Lösung: Modifiziere pop
(falls Array nur noch zu
1
4
belegt ist, halbiere & kopiere um)
Implementierung (in Java)
23
2
Datenabstraktion
2.1
Geheimnisprinzip
Kenntnis der Spezifikationen eines Moduls, Datentyp, Systems ist notwendige und hinreichende Voraussetzung für deren konkrete Benutzung.
Bsp.: Prozentuale Abstraktion
Datenabstraktion ist die Anwendung des Geheimnisprinzips auf die Darstellung komplexer Datenstrukturen:
• nicht direkt manipulierbar
• nur über (prozedurale) Schnittstelle manipulierbar (die geeignete Operationen zur Verfügung
stellt)
Einschränkung der Sichtbarkeit bei der klassenbasierten Datenabstraktion
• Das Klassenkonzept untersützt die Zusammenfassung von Daten und dem sie manipulierenden
Operationen (in einer syntaktischen Form).
• In Java kann das Verberigen der internen Repräsentation durch Sichtbarkeitsmodifikatoren erreicht werden.
–
–
–
–
public : überall
protected : innerhalb des eigenen Pakets und in den Unterklassen
private : nur innerhalb der umschließenden Klasse
default : innerhalb des Pakets
• Diese steuern die Sichtbarkeit von Attributen und Methoden
Bsp.: Stacks (als Arrays)
c l a s s Stack {
int max = 1 0 0 ;
Object [ ] S = new Object [ max ] ;
int t = 0 ;
void push ( Object o ) throws E x c e p t i o n {
i f ( t == max) throw new Exeption ( ) ;
s [ t++] = o ; // e r s t s [ t ] o z u w e i s e n und dann t i n k r e m e n t i e r e n
}
}
Object pop ( ) . . .
24
Problem: direkte Manipulation der Daten möglich.
Stack s = new Stack ( ) ;
st = 124;
s . push ( 3 ) ;
11. November 2008
2.2
Abstrakte Datentypen
Definition ADT :
Ein ADT ist eine Menge von Objekten mit einer Menge von Operationen auf diesen Objekten.
alternativ:
Ein Typ, dessen Objekte nur über die Operationen seiner Schnittstelle manipuliert werden können.
Beispiel:
Stack
Operationen :
push : ( x , S ) −> S
pop : ( S ) −> S
Objekte
Folgen (von Elementen)
↑ nach dem LIFO- Prinzip
Vorteile der Datenabstraktion
• Implementierung unabhängig vom Modell
• Sicherheit : Objekte können nicht in ungültige Zustände versetzt werden
• Flexibilität : Code kann unabhängig vom benutzten Code entwickelt werden
• Komfort : Benutzer abstrahiert von der Repräsentation
25
Reale Welt
“reelle” Objekte mit “rellen” Operatoren
↓ Modellierung
Modell abstrakte Objekte , Operationen
↓ Implementierung
Datenstruktur, Methoden
2.2.1
Explizite Schnittstellenbeschreibung in Java
“Probleme”
• Operationen müssen aus dem Quelltext der Klasse herausgesucht werden
• die Implemetierung der Methoden wird nicht vom Benutzer verborgen
9, 1, 2, 5, 7, 5,
push_left
pop_left
push_right
pop_right
Deklaration eines Schnittstellentyps
interface S t a c k I n t e r f a c e {
void push ( Object o ) ;
Object pop ( ) ;
}
• Methoden haben leeren Rumpf
• keine (non-const) Attribute
• Standardsichtbarkeit public
Im Programm:
( 1 ) F e s t l e g u n g aud e i n e k o n k r e t e Implementierung
Stack s = new Stack ( ) ;
(2) e v t l . andere
S t a c k I n t e r f a c e s = new Stack ( ) ;
26
(3) geht nicht !
S t a c k I n t e r f a c e s = new S t a c k I n t e r f a c e ( ) ;
Implementierung einer Schnittstelle
Pflichten:
1. alle Schnittstellenmethoden müssen implementiert werden
2. alle Schnittstellenmethoden müssen public sein
Rechte:
1. Parameter von Schnittstellenmethoden dürfen umbenannt werden
2. beliebige weitere Attribute/Methoden
3. Schnittstelle kann als Typ verwendet werden (aber keine Instanzen möglich)
Bemerkung:
• Eine Klasse kann mehrere Schnittstellen implementieren
• guter Stil als Typbezeichner möglichst Schnittstellentypen verwenden, Klassentypen nur bei Instanzierung
27
13. November 2008
3
ADT- Anwendungen, Implementierungen
3.1
Der ADT - Prioritätswarteschlange
Motivation:
1. Scheduling von Prozessen im Betriebssystem (benötigt Operationen)
• finde Prozess mit höchster Priorität
• erzeuge neuen Prozess (mit vorgegebener Priorität)
• verringere Priorität eines Prozesses
2. Sortieren durch wiederholes Betimmen des kleinsten Elements: benötigt Operationen
• finde Element das am kleinsten ist
• entferne dieses Element
ADT PWS (priority queue)
U:
Universum, total geordnet via ≤ (Relation)
Objekte des ADT:
endl. Teilmengen S ⊂ U
Operationen − ! −:
findmin : S → U liefert min {x ∈ S}
deletemin S 1→ S # mit S # = S \ min S
Damit:
insert : (x, S) 1→ S # mit S # = S ∪ {x} x ∈ U
Algorithm PQSort (A)
Input :
Output :
Array A [ 1 ] , . . . , A[ n ] von Elementen aus U
S o r t i e r u n g von A ( b z g l . d e r Ordnung a u f U)
PQ H;
f o r i = 1 t o n do
H. i n s e r t (A[ i ] ) ;
f o r i = 1 t o n do
p r i n t H. f i n d m i n ( ) ;
H. d e l e t e m i n ( ) ;
28
Laufzeit von PQSort:
TX Laufzeit von X ∈ {findmin, deletemin, insert}
Gesamtlaufzeit: O(n · Tinsert + n · Tfindmin + n · Tdeletemin + n)
, Damit: Gesamtlaufzeit O n2
3.1.1
Implementierung des ADT PWS (für U = N)
(a)
Implementierung der PWS als einfach verkettete Liste
Tfindmin = O(|S|), Tinsert = O(1), Tdeletemin = O(|S|)
(b)
Als Folge l unsortierter Listen mit
Liste (unsortiert) verkettet sind.
'
l − 1 haben m Elemente
1 hat ≤ m Elemente
l·n=m
29
bei denen die Minima in einer
Tfindmin = O(l)
Tinsert = O(l)
Tdeletemin = O(l + m)
Damit:
Gesamtlaufzeit : (Optimal für l = m =
√
O(n · l + n · m + n) = O(n · n)
√
n)
(c) Heaps
Ein heapgeordneter Baum für eine Menge S ⊂ U ist ein Baum auf den Elementen von S, wobei gilt:
u Kind von v ⇒ u ≥ v
Bsp.:
30
insbesondere:
Eigenschaften heapgeordneter Bäume:
• Minimum ist in der Wurzel
• Jeder Teilbaum ist Heap-g.
interface
void
Object
void
}
PrioritätsWarteSchlange {
i n s e r t ( Object o ) ;
findmin ( ) ;
deletemin ( ) ;
31
18. November 2008
3.2
Implementieruung des Heap
heapgeordneter Bäume (s.o.) | Wälder
Implementierung des PWS - Op auf H- geordneten Bäumen
* findmin trivial
* deletemin setzt die Wurzel auf ”∞” und lässt sie “absinken” bis zu einem Blatt und entfernt dieses
* insert
ausgehend von einer “freien Position” (abhängig von Details der Baumstruktur)
füge neues Objekt ein und lass es “aufsteigen”
Konkreter binärer Heap
wir betrachten:
• binär
• heap- geordnete Bäume, wo die Tiefe von Blättern sich max im 1 unterscheidet
• alle bis auf einen inneren Knoten genau zwei Kinder haben
Fakt:
Diemaximale Tiefe eines binären Heaos mit n Elementen ist O(log n)
• deletemin auf bin. Heaps:
Schreibe rechtetes Blatt in die Wurzel & lass es absinken (entferne das Blatt, mache es zum
ersten Blatt und aktualisiere das rechteste Blatt)
32
• insert auf bin. Heaps:
Schreibe das neue Element in das nächste freie Blatt & lass es aufsteigen, mache es zum rechtesten
Blatt und bestimme das neue rechte nächste freie Blatt
• wir stellen binäre Heaps so dar, dass die letzte Ebene des Baumes von links nach rechts aufgefüllt
ist
• wir “merken” uns das “rechteste” Blatt der letzten Ebene & das erste freie Blatt
Mögliche Implemetierung von binären Heaps
1. verzeigerte Struktur (später)
2. flach in einem Array
33
• Speichere bin. Heap mit n Elementen in Feld der Länge n
• Knoten mit Index i hat linke (rechte) Kind bei Index Index 2i (2i+1)
• “letztes” Element ist bei Index n gespeichert, die erste freie Position bei Index n+1
• Dynamisierung durch iteriertes Verdoppeln/Halbieren (amortisiert O(1) pro Operation)
Laufzeit:
deletemin & isert
findmin
O(log n)
O(1)
Damit Laufzeit von PWSSort (Heapsort):
O(n · log n)
Ziel:
(Effiziente) Implementierung der Operation (P,Q, PWS)
34
20. November 2008
3.3
Erweiterung des ADT PWS : VPWS
triviale Implementierung
i n t e r f a c e VPWS {
void i n s e r t ( Object o ) ;
void d e l e t e m i n ( ) ;
Object f i n d m i n ( ) ;
void meld (VPWS P ) ;
}
i n t e r f a c e PWS {
void i n s e r t ( Object o ) ;
void d e l e t e m i n ( ) ;
Object f i n d m i n ( ) ;
c l a s s NameVPWS implements VPWS {
private BinaererHeap P ;
// i r g e n d w o i s t " c l a s s BinaererHeap implements PWS{ . . . } "
void d e l e t e m i n ( ) {
P. deletemin ( ) ;
}
}
void meld (NameVPWS Q) {
solange Q nicht l e e r :
P . i n s e r t (Q. f i n d m i n ( ) ) ;
Q. d e l e t e m i n ( ) ;
}
35
Vererbung (Einschluss- Polymorphie) in Java
(Polymorphie = Vielgestaltigkeit)
Klasse (bzw. Schnittstelle) Y wird als Erweiterung der Klasse X vereinbart und erbt damit die eigenschaften von X.
Syntax:
c l a s s X { Text von X }
c l a s s Y extends X { Text von Y}
−−−−−
entspricht fast
class Y {
Text von X
Text von Y
}
Umgesetzt:
i n t e r f a c e VPWS extends PWS {
void meld ( . . . ) ;
}
bzw.
c l a s s VBinaerHeap extends BinaerHeap implements VWPS {
void meld . . .
}
36
3.4
neue Implementierung als Binomialheaps
Binomialbäume:
Bi bezeichnet einen Binomialbaum vom Grad i.
induktiv definiert:
Bsp:
Es gilt:
1. Bi hat 2i Knoten (Induktion)
2. Die Wurzel von Bi hat i Knoten
3. Die Tiefe von Bi ist i
Ein Binomialheap mit n Elementen S ist ein Wald von Binomialbäumen in denen die Elemente von S
heap- geordnet gespeichert sind. Jeder Bi kommt dabei höchstens 1x vor. Das gilt immer !
Bsp:
S = {7, 5, 1, 4, 13, 6} n = 6 = 22 + 21 ⇒ B1 und B2
37
Es gilt: wir benötigen Θ(log n)
Binomialbäume in einem Binomialheap für n Elemente
(hint: Binärdarstellung)
Die (Wurzeln) der Binomilabäume sind in einer Liste verkettet, sortiert nach ihrem Grad.
Bsp.: S = {7, 5, 1, 4, 13, 6, 15, 9, 3, 8, 27, 21, 34, 99} n = 14 = 23 + 22 + 21
Implementierung der VPWS- Operationen
(a) meld
• durchlaufe Wurzellisten P und Q (angefangen beim kleinsten Grad)
• zu jedem Zeitpunkt stellen wir sicher, dass es im Resultat nur einen Baum pro Grad gibt
• es gibt jeweils maximal einen Baum C der aus dem vorherigen Schritt als Übertrag kommt
• sei A der aktuelle Binomialbaum von P
sei B der aktuelle Binomialbaum von Q
– Fall 1: es gibt kein C
∗ deg A < deg B : schreibe A in die Wurzelliste von P ∪ Q , ersetze A durch seinen
Nachfolger
∗ deg A > deg B : umgekehrt
∗ deg A = deg B
· min B < min A : mache A zu Kind der Wurzel B und setze das Ergebnis als Übertrag
C
Ersetze A (und B) durch ihre Nachfolge
· min A < min B : umgekehrt
– Fall 2: es gibt ein C (=⇒ Übungsaufgabe)
38
25. November 2008
verschmelzen
n=4
8 + 4 + 2 = 23 + 22 + 21
Implemetierung der restlichen Heap- Op
• findmin(P)
– Durchlaufen der Wurzelliste
O(log |P |) Zeit
• insert(P,x)
– durch meld (P,{x})
O(log |P |) Zeit
• deletemin(P)
– Finde Bi in der Wurzelliste der Minimum speichert (in seiner Wurzel), entferne Bi aus
der Wurzelliste, entferne Wurzel von Bi (resultiert in {B0 , . . . , Bi−1 }) und erzeugen damit
neuen Binärheap.
Verschmelzen den neuen & den alten Heap.
O(log |P |) Zeit
3.5
Lokalisierung von Einträgen in Datenstrukturen
Typischerweise “wissen” Einträge in einer DS wo sie in der Struktur gepeichert sind.
Sonst ist das manipulieren von Einträgen in der DS “schwierig” (d.h. unmöglich/ineffizient).
39
Bsp.:
• Löschen von einträgen aus einer PWS
• Ändern der Priorität von Einträgen einer PWS
mögliche Lösung
Schnittstellenmethoden zum Einfügen von Objekten liefern einen “Zeiger” auf den Eintrag der Objekte
in der DS.
z.B.:
c l a s s PWSEintrag {
int
Prioritaet ;
Object E i n t r a g ;
int
Position ;
}
3.6
Bäume
mathematischer Kontext
Kreisfreier zusammenhängender Graph
typischerweise gibt es eine (totale Ordnung)
auf den Kindern eines Knotens.
Informatik Kontext
gerichteter Graph T = (V, E)
Wurzel w ∈ V
(u, v) ∈ E
v heißt Kind (Nachfolger) von u
u heißt Erziehungsberechtigter (Vorgänger) von v
Bäume als ADT
ADT Knoten
• speichert Objekte
• Manipulation/Zugriff
void setInfo (Object o);
Object getInfo();
40
3.6.1
Schnittstelle zum ADT Baum (Auswahl)
Knoten
Knoten
boolean
Knoten
int
getRoot ( ) ;
g e t P a r e n t ( Knoten k ) ;
a L e a f ( Knoten k ) ;
g e t C h i l d ( Knoten k , int i ) ;
g e t D e g r e e ( Knoten k ) ;
41
27. November 2008
Implementierung von Bäumen
1.
Verkettete Struktur auf den Knoten
Platzbedarf für Baum mit n Knoten : O(n)
Spezialfall: k- näre Bäume (k > 2)
• jeder Knoten hat ≤ k Kinder
• falls jeder inäre Knoten genau k- Kinder hat, heißt der Baum wohl
• bei geordneten Binärbäumen heißt das
– 1. Kind auch linkes Kind
– 2. Kind: auch rechtes Kind
Bsp. k=3
Höhe des Baumes ist h.
h=0
30 = 1
h=1
30 + 3 · 30 = n0 + 3n0 = 4
h=2
4 + 3 · 3 = 13
h=3
13 + 9 · 3 = 40
h
&
i=0
qi =
q n+1 − 1
q−1
42
2.
Arraydarstellung k- närer Bäume
h+1
k- närer Baum der Höhe h wird im Array mit k k−1−1 Elementen abgespeichert.
Das i-te Kind (1 ≤ i ≤ k) eines Knotens der beim j-ten Eintrag gespeichert ist, wird im
(k · j + i) Eintrag abgelegt.
Zugriff auf Elternknoten durch gannzahlige Division.
(evtl. Problem)
Das Array kann exponentiell groß (im Vergleich zum Baum) sein.
Algorithmen auf Bäumen
• Traversierung von Bäumen
inorder:
besuche erst den linken Teilbaum unter der Wurzel, dann die Wurzel, dann ihren
rechten Teilbaum
preorder: besuche zuerst die Wurzel, dann dann links, dann rechts
postorder: besuche erst links, dann rechts, dann Wurzel
(1) Anwendung von inorder- Traversierung
T = (V, E)
in : V → N inorder Traversierung
h : V → N Höhenfunktion
'
V → R2
ϕ=
∪ → (in(u), h(u))
ist eine kreuzungsfreie Zeichnung von T in der Ebene.
43
(2) Arithmetische Ausdrücke (auf ganzen Zahlen)
(über {+, −})
rekursiv definiert:
(i)
x ∈ Z ist ein arithm. Ausdruck
(ii)
e, f arithmetische Ausdrücke
⇒ (e + f ) sind arithm. Ausdrücke
(e − f )
z.B.
12345+3456.
Darstellung als Ausdrucksbaums z.B.: ((2 + 5) − (7 + 12))
Auswertung durch postorder- Traversierung.
Umgekehrte Polnische Notation (UP- Notation): 2, 5, +, 7, 12, +, −
44
02. Dezember 2008
3.6.2
weitere Anwendungen: Spielbäume
TicTacToe
Beginnt der Kreis- Spieler in der Mitte, hat der Kreuz- Spieler acht verschiedene Möglichkeiten sein
Kreuz zu setzen.
Dann hat der Kreis- Spieler sieben Möglichkeiten. ...
Nehme als Root das leere Spielfeld. Der erste Nachfolger sind neun Knoten, alle Möglichkeiten für die
erste Belegung. Die Nachfolger dieser Knoten sind jeweils die acht Möglichkeiten, ein Kreuz zu setzen.
usw.
Es gibt besondere Knoten, bei denen das Spiel zu Ende ist. Dieser Knoten ist ein Blatt. Entweder, das
Spiel wird gewonnen oder es gibt ein Remis.
Zunächst werden alle Nachfolger der Wurzel erzeugt, erfüllen sie die Anforderung? Nun werden die
Kinder erzeugt, usw.
⇒ Breitensuche
3.6.3
Breitensuche in Bäumen
Traversiere den Baum in der folgenden Reihenfolge.
(Hier ein Baum mit immer drei Nachfolgern)
1. Wurzel (r)
2. Kinder der Wurzel (seien diese w1 , w2 , w3 )
3. Kinder der Knoten aus 2. ; erst die Kinder von w1 , dann die Kinder von w2 , dann die Kinder
von w3
4. Deren Kinder. Nach Methodik von 3.
Diese Knoten werden in einer Liste verwaltet:
r, w1 , w2 , w3 , w11 , w12 , w13 , w21 , w22 , w23 , w31 , w32 , w33
Dies ist eine Warteschlange (Queue).
45
3.6.4
Tiefensuche in Bäumen
Traversierung mittels Stack
Erst ganz links bis nach unten, dann von “ganz unten” zum ersten Vater, dessen nächstes Kind, dann
eins hoch, nächstes Kind,...
3.7
ADT Wörterbuch (Dictionary)
Verwalten einer endlichen Teilmenge S ⊂ U eines Universums U
Operationen :
find(k,S)
bestimme, ob k ∈ S
insert(k,S) füge k zu S hinzu
delete(k,S) lösche k aus S
Bemerkung:
• Gegebenenfalls Fehlerbehandlung (z.B. bei delete)
• im Allgemeinen is S eine Multimenge
Vielfache Anwendungen!
3.7.1
Implementierung des ADT Wörterbuch
1. Verkettete Liste
Platzbedarf θ(|S|)
Laufzeit:
insert θ(1) ggf. θ(|S|)
find θ(|S|)
delete θ(1) falls falls Zeiger in die Liste zeigt
sonst θ(|S|)
2. Hashing (Streuspeicherung)
1.Idee Finde “gute” Abbildung h1 : U → N
2.Idee Finde “gute” Abbildung h2 : N → [1, . . . N ]
Speichere S in ein Array T [1, . . . , N ]
und zwar speichern x ∈ U in T [h2 (h1 (x))]
46
04. Dezember 2008
3.7.2
Implementierung durch Hashing
U
Universum
S⊂U
h1 : U → N
h2 : N → [1, . . . N ]
h2 (n) = 1 + (n mod 4)
3.7.3
Hashcodes
1. Zeichenketten s = s1 . . . sk ∈
(a) Länge
!0
!
h1 (si )| |
'!
→N
0
wobei h1 =
σi 1→ i
(b) h! (s) =
!∗
!
{σ0 , . . . , σl }
z.B.: ASCII
s = AFFE
!
| | = 256, h1 (A) = 65 , h2 (F ) = 70 , h3 (E) = 69 , h1 (s) = (((65 · 256 + 70) · 256) + 70) · 256 + 69
2. Floatingpoint- Zahlen
(a) h1 (x) = 4x5
(b) h1 (x) = Mantisse von x als Integer
Kompressionsverfahren
Idee:
h2 soll S “möglichst geleichmäßig” auf [1, . . . N ] aufteilen.
Bsp.:
1. h2 (x) = 1 + (x mod N )
2. h2 (x) = 1 + (ax + b mod N )a'=0(mod N ), a,b∈N
47
Typischerweise ist N eine Primzahl
Hashing mit Verkettung
U
Universum
|U | = u
S ⊂ U, |S| = n
Hashfunktion in Tabelle T der Größe N. (Hashfunktion ist fest)
Hashtabelle mit N Einträgen
h : U → [0, . . . , N − 1]
n- elementige Teilmengen von U sollen verwaltet werden.
Idee:
Speichere x ∈ S an Position h(x) in T
x1 , x2 ∈ S kollidieren (bzgl. h), falls h(x1 ) = h(x2 )
Behandlung von Kollisionen durch Verkettung
T [i] zeigt af eine verkettete Liste in der alle x ∈ S mit h(x) = i gespeichert sind.
Bsp.:
• Einfügen/Löschen/Suchen eines Eintrags e mit Schlüssel k
• berechne i = h(k)
• Einfügen / L / S von e in Liste T [i] wie oben
Analyse:
Platzbedarf : O(N ) +
O(n)
= O(n + N )
" #$ %
" #$ %
Array T Knoten der Liste
48
Laufzeit (für Eintrag mit Schlüssel k)
hängt ab von
1. der Zeit, die zur Berechnung von h() benötigt wird.
2. der Länge der Liste die den Eintrag speichert.
Cs (k) = {y ∈ S|h(y) = h(k)}
Falls h in O(n) Zeit ausgewertet werden kann, benötigen alle Operationen θ(1 + |Cs (k)|) Zeit.
Analyse der mittleren Laufzeit von Hashing mit Verkettung
U
sei fest. (U sei endlich, |U | = u)
n
sei fest.
h
sei fest.
S ⊂ U wird zufällig (gleichverteilt) gewählt.
.
/
u
U hat
= |W | viele n- elementigen Teilmengen
n
Für V ⊂ U mit |V | = n fest, gilt:
P r(S
W
= V ) = pv = .
1
1
/=
|W |
u
n
= {S ⊂ U | |S| = n}
Für k ∈ U fest betrachten wir Cs (k) Zufallsvariable und interessieren uns für
E [CS (K)] =
&
v∈W
!
v∈W
pv |Cv (k)| =
[CS (K)]
Beweis:
49
1
|W |
[CS (K)]
'
1 y∈S
=
1=
iS (y) mit iS (y) =
0 sonst
y∈S, h(k)=h(y)
y∈U, h(k)=h(y)
&
&
Damit
E [CS (K)]
=
=
&
1
·
[CV (K)]
|W |
V ∈W
&
&
1
·
·
|W |
V ∈W
=
&
V ∈W
=
&
y∈U, h(k)=h(y)
& $
·
V ∈W
=
·
v(y)
y∈U, h(k)=h(y)
E[iS (y)]
&
pv
$%"#
1
v(y)
|W |
für y fest
%"
#
p(v) · iv (y)
y∈U, h(k)=h(y)
&
y∈U, h(k)=h(y)
n
u
,u−1-
, n- =
E[iS (y)] = 1 · P r(iS (y) = 1) + 0 · P r(is (y) = 0) = P r(y ∈ S) = n−1
u
(*)
50
n
u
09. Dezember 2008
3.7.4
S∈
Zufallsexperiment
,U n
wird zufällig (unter Gleichverteilung gewählt)
Analyse Zufallsvariable (für x ∈ U )
|CS (x)| = |{y ∈ S|h(x) = h(y)}| (s.o.)
Fortsetzung von (*)
=⇒ E [CS (x)]
=
&
y∈U, h(k)=h(y)
=
=
n
·
u
n
u
&
1
y∈U, h(k)=h(y)
n
· |CS (x)| = |{y ∈ S|h(x) = h(y)}|
u
Def.:
Eine Hashfunktion h : U → [1, . . . , N − 1] heißt fair, falls
|{y ∈ U |h(y) = i}| ≤
Falls h fair:
Zusammengefasst:
n 1u2
E [CS (x)] ≤ ·
≈
u
N
1u2
N
∀0 ≤ i ≤ N
Belegungsfaktor der Hashtabelle
$%"#
n
N
Die erwartete Zugriffszeit für Hashing mit Verkettung (bei Verwendung einer fairen Hashfunktion) bei
der Verwaltung von n- elementigen Teilmengen von U in einer Tabelle mit N Einträgen ist
Bsp:
3
n4
O 1+
N
'
[0, . . . , u − 1] → [0, . . . , N − 1]
h(x) =
x 1→ x mod N
ist fair.
Beweis:
51
(a)
mit N = θ(n) erhalten wir θ(1) (erwartete) Zugriffszeit und θ(n) Speicher.
(unter den bekannten Annahmen)
(b)
Falls n nicht bekannt ist, kann durch Verdoppeln, bzw. Halbieren der Tabellengröße (inkl.
Umkopieren) θ(1) amortisierte erwartete Laufzeit bei θ(n) Platz erreicht werden.
Alternative: Universelles Hashing
Idee: Wählen h beim Aufbau der Struktur (unter Gleichverteilung) aus einer Menge von “guten”
Hashfunktionen H.
Analyse:
Sei S ⊂ U mit |S| = n und x ∈ U fest.
Für h ∈ H sei
Cx (h)
=
=
{y ∈ S|h(x) = h(y)}
&
δxy (h)
y∈S
'
1 h(x) = h(y)
mit δxy (h) =
0 sonst
E [|Cx (h)|]

= E
=
&
&
y∈S

δxy (h)
[δxy (h)]
y∈S
=
&
(P r (h(x) = h(y)))
y∈S
#
|{h ∈ H|h(x) = h(y)}|
1
=
mit P r(h(x) = h(y)) =
|H|
N
"
$
& 1
n
E [|Cx (h)|] ≤
=
N
N
!
y∈S
Def.:
U
Eine Menge H ⊂ {0, . . . , N − 1}
von Hashfunktionen heißt universell, falls ∀x, y ∈ U mit x 6= y
|{h ∈ H|h(x) = h(y)}| ≤
52
|H|
N
Damit:
Die erwartete Zugriffszeit für Hashing mit Verkettung bei zufälliger Wahl von h aus einer universellen
Familie von H- Funktionen bei der Verkettung
3 n 4 einer (jeder!) (festen) n- elementigen Teilmenge S ⊂ U
in einer Tabelle mit N Einträgen ist θ 1
bei θ(n + N +
|h|
) Speicher.
"#$%
N
Platzbedarf, um h zu codieren
3.7.5
Universelle Hashfunktionen
1. {0, . . . , N − 1}
wertbar.
{0,...,u−1}
ist universell, aber nicht platsparend repräsentierbar bzw. effizient aus-
2. Angenommen, x ∈ U kann in eindeutiger Weise als (r + 1)- Tupel x = (x1 , . . . , xr ) mit 0 ≤ xi <
t ∀i und eine Primzahl t. (z.B. t=257 und x wird byteweise gelesen).
a
$
%"
#
r+1
Für (a0 , . . . , ar ) ∈ {0, . . . t − 1}
definieren wir die Hashfunktion
ha (x)
= h(a0 ,...,ar ) (x0 , . . . , xr )
&
=
ai xi mod N
0≤i≤r
9
:
r+1
Dann ist H = ha |a ∈ {0, . . . , N − 1}
universell.
Bemerkung:
(a)
Zum Abspeichern von h(a0 ,...,ar ) wird θ(r) Platz benötigt.
(b)
Zum Berechnen von h(a0 ,...,ar ) (x0 , . . . xr ) wird θ(r) Zeit benötigt.
53
11. Dezember 2008
Fortsetzung “Universelle Hashfunktion”:
U = {0, . . . , u − 1} u = N r+1
N Primzahl (Größe der Hashtabelle)
x ∈ U ⇔ (x0 , . . . , xr ) mit 0 ≤ xi ≤ N
r+1
Zu (a0 , . . . , ar+1 ) ∈ {0. . . . , N − 1}
definieren wir
'
U → {0. . . . , N − 1}
h2 =
!r
x = (x0 , . . . , xr ) 1→ i=0 ai xi mod N
9
:
r+1
Dann ist H = h0 |a ∈ {a0 , . . . N − 1}
universell.
Bemerkung:
1. Um h ∈ R zufällig zu wählen müssen r + 1 Zufallszahlen im Intervall [0, N − 1] erzeugt werden.
2. Um (bei festen h ∈ R) zu x = (x0 , . . . , xi ) h(x) auszuwerten, werden r + 1 Multiplikationen (und
Addition) mittels N benötigt.
Angenommen x, y ∈ U mit x 6= y und ha (x) = ha (y) (*)
Da x 6= y muss (x0 , . . . , xr ) + (y0 , . . . , yr )
Wir nehmen oBdA an, dass x0 6= y0 .
(*)
r
&
ai xi =
i=0
a0 (x0 − y0 )
r
&
ai yi (mod N)
i=0
= a0 x0 − a0 y0
r
&
=
ai (yi − xi ) (mod N)
i=0
Bei gegebenen x und y gibt jede Wahl von a1 , . . . ar auf der rechten Seite eine feste Zahl C. Da N prim
ist und x0 6= y0 gibt es genau eine Möglichkeit, a0 zu wählen, um die Gleichnug zu erfüllen.
−1
a0 = C · (x0 − y0 )
(mod N)
Es gibt genau N Möglichkeiten a1 , . . . ar zu wählen.
%
'
|H|
Damit |{ha |ha (x) = h(y)}| = N r =
N
&
(
r
54
3.8
ADT Geordnetes Wörterbuch
Ziel: Verwaltung einer Teilmenge S von Elementen aus einem total geordneten Universum U ( wir
werden die Ordnung mit ≤ bezeichnen) unter dem Operation
find(x)
bestimme, ob x ∈ S
insert(x)
setze S ← S ∪ {x}
delete(x)
setze S ← S\{x}
min
bestimme min S
max
bestimme max S
succ(x)
bestimme das kleinste Element S, mit der Eigenschaft: min {y ∈ S|y ≥ x}
pred(x)
bestimme das größte Element S, mit der Eigenschaft: max {y ∈ S|y ≤ x}
Bemerkung:
Wir nehmen an, dass die Ordunungsrelation in O(1) Zeit entschieden werden kann.
3.8.1
Implementation des ADT G.W.
1. Suchbäume (später)
2. Geordnete nach ≤ verkettete Liste
3. Skip- Listen
• Hierarchische DS mit r Stufen L1 , . . . , Lr
• In Stufe Li ist die Menge Si ⊂ S in einer Liste gespeichert, wobei
S = S1 ≥ S2 ≥ S3 ≥ . . . ≥ Sr = ∅
• Für z ∈ Si+1 ∩ Si gibt es einen Zeigervon Eintrag für z in Li
3.8.2
Skip Liste:
Ist ähnlich zu verketten Listen. Die Daten werden in Containern abgelegt. Jeder Container enthält
einen Schlüssel und einen Zeiger auf den nächsten Container. Es können jedoch auch Zeiger auf andere
Container enthalten sein, welche nicht direkt nachfolgen. Demnach können auch Schlüssel übersprungen
werden. Die Zeiger werden von 0 bis h durchnummeriert, wobei h die Höhe ist; h ist um 1 kleiner als
die Anzahl der Zeiger, die ein Container enthält.
55
Suche in einer Skip- Liste (find, succ, pred) Suche nach q ∈ U
• bestimme in Li das Intervall ∆i , welches q enthält, durch lineare Suche
• die lineare Suche in Li beginnt beim Anfangspunkt des Intervalls ∆i+1
Bestimmung der Si (mod r)
S1 = S
i=1
while S1 6= ∅
Si+1 = {z ∈ Si |Münzwurf für z zeigt Kopf}
i=i+1
r=1
Löschen von z ∈ S
Lösche z aus allen Li mit z ∈ Si (inklusive der vertikalen Zeiger)
Einfügen von z ∈ S
• wirf eine Münze bis zm ersten Mal Zahl auftaucht
– j = # Münzwürfe -1
• füge z in die Liste L1 , . . . , Lj+1 ein.
– unterteile ∆i bei z
– verkette die Vorkommen von z in aufeinanderfolgenden Stufen vertikal
• falls j + 1 > r erzeuge
– j + 1 − r neue Stufen die nur z enthalten
56
Herunterladen