Datenstrukturen und Algorithmen

Werbung
Datenstrukturen und Algorithmen
Übung 5
Alexander Pilz, Daniel Hupp, Lukas Humbel
FS 2017
1
Programm von heute
1 Feedback letzte Übung
2 Wiederholung Theorie
3 Programmieraufgabe
2
Amortisierte Analyse: push back
Strategie: verdoppeln wenn Array voll ist.
3
Amortisierte Analyse: push back
Strategie: verdoppeln wenn Array voll ist.


1
wenn Array noch nicht voll
ti = 
2n + n + 1 = 3n + 1 sonst
3
Amortisierte Analyse: push back
Strategie: verdoppeln wenn Array voll ist.


1
wenn Array noch nicht voll
ti = 
2n + n + 1 = 3n + 1 sonst
Finde Potential function so dass amortisierte Kosten konstant sind:
ai = ti + Φi − Φi−1
3
Amortisierte Analyse: push back
Strategie: verdoppeln wenn Array voll ist.


1
wenn Array noch nicht voll
ti = 
2n + n + 1 = 3n + 1 sonst
Finde Potential function so dass amortisierte Kosten konstant sind:
ai = ti + Φi − Φi−1
n
Φi = 6·Anzahl Elemente in der oberen Hälfte des Arrays ( +1, . . . , n)
2
3
Amortisierte Analyse: push back
Strategie: verdoppeln wenn Array voll ist.


1
wenn Array noch nicht voll
ti = 
2n + n + 1 = 3n + 1 sonst
Finde Potential function so dass amortisierte Kosten konstant sind:
ai = ti + Φi − Φi−1
n
Φi = 6·Anzahl Elemente in der oberen Hälfte des Arrays ( +1, . . . , n)
2
⇒ 7 ≥ ai ( in beiden Fällen)
3
Amortisierte Analyse: pop back
Strategie: halbieren wenn Array viertel leer ist.
4
Amortisierte Analyse: pop back
Strategie: halbieren wenn Array viertel leer ist.


1
ti =  n

2
+
n
4
=
3
4n
wenn Array mehr als viertel voll ist
sonst
4
Amortisierte Analyse: pop back
Strategie: halbieren wenn Array viertel leer ist.


1
ti =  n

2
+
n
4
=
3
4n
wenn Array mehr als viertel voll ist
sonst
Finde Potential function so dass amortisierte Kosten konstant sind:
ai = ti + Φi − Φi−1
4
Amortisierte Analyse: pop back
Strategie: halbieren wenn Array viertel leer ist.


1
ti =  n

2
+
n
4
=
3
4n
wenn Array mehr als viertel voll ist
sonst
Finde Potential function so dass amortisierte Kosten konstant sind:
ai = ti + Φi − Φi−1
n
φi = 3 · Anzahl Elemente in der unteren Hälfte des Arrays (1, . . . , )
2
4
Amortisierte Analyse: pop back
Strategie: halbieren wenn Array viertel leer ist.


1
ti =  n

2
+
n
4
=
3
4n
wenn Array mehr als viertel voll ist
sonst
Finde Potential function so dass amortisierte Kosten konstant sind:
ai = ti + Φi − Φi−1
n
φi = 3 · Anzahl Elemente in der unteren Hälfte des Arrays (1, . . . , )
2
⇒ 4 ≥ ai ( in beiden Fällen)
4
Randomisierte Skipliste
Idee: Füge jeweils einen Knoten mit zufälliger Höhe H ein, wobei
1
P(H = i) = 2i+1
.
3
2
1
0
x1
x2
x3
x4
x5
x6
x7
x8
∞
5
Skiplisten Interface
class SkipList {
public:
SkipList();
~SkipList();
void insert( const T& newValue );
void erase( const T& eraseValue );
};
6
I Definiere einen Knoten
x
7
I Definiere einen Knoten
struct Node {
T value;
// pointers to successor nodes
std::vector<Node*> forward;
x
Node( const T& v, int level ):
value(v),
forward(level,nullptr) {}
};
7
Implementiere insert
insert new value
finde erst kleineren
node
wähle zufällige Anzahl
von Leveln aus
erstelle neuen node
setze pointers von den
vorhärigen nodes und
neuem node
8
Implementiere insert
insert new value
finde erst kleineren
node
wähle zufällige Anzahl
von Leveln aus
erstelle neuen node
setze pointers von den
vorhärigen nodes und
neuem node
void insert( const T& value ) {
std::vector<Node*> preds =
predecessors( value );
const int n = randomLevel();
Node* node = new Node( value, n);
for( int i = 0; i<n; ++i ) {
node->forward[i] =
preds[i]->forward[i];
preds[i]->forward[i] = node;
}
}
8
Implementiere erase
erase( value )
finde ersten
kleineren node
überprüfe ob
nächster node den
Wert value hat
Pointers
entsprechend
setzen
gegebnenfals
node löschen
9
Implementiere erase
erase( value )
void erase( const T& val ) {
finde ersten
std::vector<Node*> preds =
kleineren node
predecessors( val );
überprüfe ob
nächster node den Node* n = preds[0]->forward[0];
while( n!=nullptr && n->value==val ) {
Wert value hat
for( int i = 0; i<nodeLevel(n); ++i)
Pointers
preds[i]->forward[i] = n->forward[i];
entsprechend
delete n;
setzen
n = preds[0]->forward[0];
gegebnenfals
}
node löschen
}
9
2. Wiederholung Theorie
10
Beispiele guter Hashfunktionen
h(k) = k mod m, m Primzahl
√
h(k) = bm(k · r − bk · rc)c, r irrational, besonders gut: r =
5−1
2 .
11
Offene Hashverfahren
Speichere die Überläufer direkt in der Hashtabelle mit einer
Sondierungsfunktion s(j, k) (0 ≤ j < m, k ∈ K)
Tabellenposition des Schlüssels entlang der Sondierungsfolge
S(k) := (h(k) − s(0, k) mod m, . . . , (h(k) − (m − 1, k)) mod m
12
Algorithmen zum Open Addressing
search(k) Durchlaufe Tabelleneinträge gemäss S(k). Wird k
gefunden, gib true zurück. Ist die Sondierungsfolge zu Ende oder
eine leere Position erreicht, gib false zurück.
insert(k) Suche k in der Tabelle gemäss S(k). Ist k nicht
vorhanden, füge k an die erste freie Position in der Sondierungsfolge
ein. 1
delete(k) Suche k in der Tabelle gemäss S(k). Wenn k gefunden,
markiere Position von k mit einem deleted-flag.
1 Als
frei gilt auch eine nicht leere Position mit deleted flag.
13
Lineares Sondieren
s(j, k) = j ⇒
S(k) = (h(k) mod m, (h(k) − 1) mod m, . . . , (h(k) + 1) mod m)
14
Lineares Sondieren
s(j, k) = j ⇒
S(k) = (h(k) mod m, (h(k) − 1) mod m, . . . , (h(k) + 1) mod m)
Beispiel m = 7, K = {0, . . . , 500}, h(k) = k mod m.
Schlüssel 12
0
1
2
3
4
5
6
Lineares Sondieren
s(j, k) = j ⇒
S(k) = (h(k) mod m, (h(k) − 1) mod m, . . . , (h(k) + 1) mod m)
Beispiel m = 7, K = {0, . . . , 500}, h(k) = k mod m.
Schlüssel 12 , 53
0
1
2
3
4
5
12
6
Lineares Sondieren
s(j, k) = j ⇒
S(k) = (h(k) mod m, (h(k) − 1) mod m, . . . , (h(k) + 1) mod m)
Beispiel m = 7, K = {0, . . . , 500}, h(k) = k mod m.
Schlüssel 12 , 53 , 5
0
1
2
3
4
5
53
12
6
Lineares Sondieren
s(j, k) = j ⇒
S(k) = (h(k) mod m, (h(k) − 1) mod m, . . . , (h(k) + 1) mod m)
Beispiel m = 7, K = {0, . . . , 500}, h(k) = k mod m.
Schlüssel 12 , 53 , 5 , 15
0
1
2
3
4
5
5
53
12
6
Lineares Sondieren
s(j, k) = j ⇒
S(k) = (h(k) mod m, (h(k) − 1) mod m, . . . , (h(k) + 1) mod m)
Beispiel m = 7, K = {0, . . . , 500}, h(k) = k mod m.
Schlüssel 12 , 53 , 5 , 15 , 2
0
1
15
2
3
4
5
5
53
12
6
Lineares Sondieren
s(j, k) = j ⇒
S(k) = (h(k) mod m, (h(k) − 1) mod m, . . . , (h(k) + 1) mod m)
Beispiel m = 7, K = {0, . . . , 500}, h(k) = k mod m.
Schlüssel 12 , 53 , 5 , 15 , 2 , 19
0
1
2
3
4
5
15
2
5
53
12
6
Lineares Sondieren
s(j, k) = j ⇒
S(k) = (h(k) mod m, (h(k) − 1) mod m, . . . , (h(k) + 1) mod m)
Beispiel m = 7, K = {0, . . . , 500}, h(k) = k mod m.
Schlüssel 12 , 53 , 5 , 15 , 2 , 19
0
1
2
3
4
5
19
15
2
5
53
12
6
14
Quadratisches Sondieren
s(j, k) = dj/2e2 (−1)j
S(k) = (h(k) + 1, h(k) − 1, h(k) + 4, h(k) − 4, . . . ) mod m
15
Quadratisches Sondieren
s(j, k) = dj/2e2 (−1)j
S(k) = (h(k) + 1, h(k) − 1, h(k) + 4, h(k) − 4, . . . ) mod m
Beispiel m = 7, K = {0, . . . , 500}, h(k) = k mod m.
Schlüssel 12
0
1
2
3
4
5
6
Quadratisches Sondieren
s(j, k) = dj/2e2 (−1)j
S(k) = (h(k) + 1, h(k) − 1, h(k) + 4, h(k) − 4, . . . ) mod m
Beispiel m = 7, K = {0, . . . , 500}, h(k) = k mod m.
Schlüssel 12 , 53
0
1
2
3
4
5
12
6
Quadratisches Sondieren
s(j, k) = dj/2e2 (−1)j
S(k) = (h(k) + 1, h(k) − 1, h(k) + 4, h(k) − 4, . . . ) mod m
Beispiel m = 7, K = {0, . . . , 500}, h(k) = k mod m.
Schlüssel 12 , 53 , 5
0
1
2
3
4
5
53
12
6
Quadratisches Sondieren
s(j, k) = dj/2e2 (−1)j
S(k) = (h(k) + 1, h(k) − 1, h(k) + 4, h(k) − 4, . . . ) mod m
Beispiel m = 7, K = {0, . . . , 500}, h(k) = k mod m.
Schlüssel 12 , 53 , 5 , 15
0
1
2
3
4
5
6
53
12
5
Quadratisches Sondieren
s(j, k) = dj/2e2 (−1)j
S(k) = (h(k) + 1, h(k) − 1, h(k) + 4, h(k) − 4, . . . ) mod m
Beispiel m = 7, K = {0, . . . , 500}, h(k) = k mod m.
Schlüssel 12 , 53 , 5 , 15 , 2
0
1
15
2
3
4
5
6
53
12
5
Quadratisches Sondieren
s(j, k) = dj/2e2 (−1)j
S(k) = (h(k) + 1, h(k) − 1, h(k) + 4, h(k) − 4, . . . ) mod m
Beispiel m = 7, K = {0, . . . , 500}, h(k) = k mod m.
Schlüssel 12 , 53 , 5 , 15 , 2 , 19
0
1
2
15
2
3
4
5
6
53
12
5
Quadratisches Sondieren
s(j, k) = dj/2e2 (−1)j
S(k) = (h(k) + 1, h(k) − 1, h(k) + 4, h(k) − 4, . . . ) mod m
Beispiel m = 7, K = {0, . . . , 500}, h(k) = k mod m.
Schlüssel 12 , 53 , 5 , 15 , 2 , 19
0
1
2
19
15
2
3
4
5
6
53
12
5
15
Double Hashing
Zwei Hashfunktionen h(k) und h0 (k). s(j, k) = j · h0 (k).
S(k) = (h(k) − h0 (k), h(k) − 2h0 (k), . . . , h(k) − (m − 1)h0 (k)) mod m
16
Double Hashing
Zwei Hashfunktionen h(k) und h0 (k). s(j, k) = j · h0 (k).
S(k) = (h(k) − h0 (k), h(k) − 2h0 (k), . . . , h(k) − (m − 1)h0 (k)) mod m
Beispiel:
m = 7, K = {0, . . . , 500}, h(k) = k mod 7, h0 (k) = 1 + k mod 5.
Schlüssel 12
0
1
2
3
4
5
6
Double Hashing
Zwei Hashfunktionen h(k) und h0 (k). s(j, k) = j · h0 (k).
S(k) = (h(k) − h0 (k), h(k) − 2h0 (k), . . . , h(k) − (m − 1)h0 (k)) mod m
Beispiel:
m = 7, K = {0, . . . , 500}, h(k) = k mod 7, h0 (k) = 1 + k mod 5.
Schlüssel 12 , 53
0
1
2
3
4
5
12
6
Double Hashing
Zwei Hashfunktionen h(k) und h0 (k). s(j, k) = j · h0 (k).
S(k) = (h(k) − h0 (k), h(k) − 2h0 (k), . . . , h(k) − (m − 1)h0 (k)) mod m
Beispiel:
m = 7, K = {0, . . . , 500}, h(k) = k mod 7, h0 (k) = 1 + k mod 5.
Schlüssel 12 , 53 , 5
0
1
2
3
4
5
53
12
6
Double Hashing
Zwei Hashfunktionen h(k) und h0 (k). s(j, k) = j · h0 (k).
S(k) = (h(k) − h0 (k), h(k) − 2h0 (k), . . . , h(k) − (m − 1)h0 (k)) mod m
Beispiel:
m = 7, K = {0, . . . , 500}, h(k) = k mod 7, h0 (k) = 1 + k mod 5.
Schlüssel 12 , 53 , 5 , 15
0
1
2
3
4
5
5
53
12
6
Double Hashing
Zwei Hashfunktionen h(k) und h0 (k). s(j, k) = j · h0 (k).
S(k) = (h(k) − h0 (k), h(k) − 2h0 (k), . . . , h(k) − (m − 1)h0 (k)) mod m
Beispiel:
m = 7, K = {0, . . . , 500}, h(k) = k mod 7, h0 (k) = 1 + k mod 5.
Schlüssel 12 , 53 , 5 , 15 , 2
0
1
15
2
3
4
5
5
53
12
6
Double Hashing
Zwei Hashfunktionen h(k) und h0 (k). s(j, k) = j · h0 (k).
S(k) = (h(k) − h0 (k), h(k) − 2h0 (k), . . . , h(k) − (m − 1)h0 (k)) mod m
Beispiel:
m = 7, K = {0, . . . , 500}, h(k) = k mod 7, h0 (k) = 1 + k mod 5.
Schlüssel 12 , 53 , 5 , 15 , 2 , 19
0
1
2
3
4
5
15
2
5
53
12
6
Double Hashing
Zwei Hashfunktionen h(k) und h0 (k). s(j, k) = j · h0 (k).
S(k) = (h(k) − h0 (k), h(k) − 2h0 (k), . . . , h(k) − (m − 1)h0 (k)) mod m
Beispiel:
m = 7, K = {0, . . . , 500}, h(k) = k mod 7, h0 (k) = 1 + k mod 5.
Schlüssel 12 , 53 , 5 , 15 , 2 , 19
0
1
2
3
4
5
19
15
2
5
53
12
6
16
3. Programmieraufgabe
17
Finden eines Sub-Arrays
Gegeben: Zwei Felder A = (a0 , . . . , an−1 ) and B = (b0 , . . . , bk−1 )
mit natürlichen Zahlen
Aufgabe: Finde Position von B in A.
18
Finden eines Sub-Arrays
Gegeben: Zwei Felder A = (a0 , . . . , an−1 ) and B = (b0 , . . . , bk−1 )
mit natürlichen Zahlen
Aufgabe: Finde Position von B in A.
Naiv: Gehe durch A, vergleiche die k folgenden Einträge mit B
18
Finden eines Sub-Arrays
Gegeben: Zwei Felder A = (a0 , . . . , an−1 ) and B = (b0 , . . . , bk−1 )
mit natürlichen Zahlen
Aufgabe: Finde Position von B in A.
Naiv: Gehe durch A, vergleiche die k folgenden Einträge mit B
O(nk) Vergleiche
18
Finden eines Sub-Arrays
Gegeben: Zwei Felder A = (a0 , . . . , an−1 ) and B = (b0 , . . . , bk−1 )
mit natürlichen Zahlen
Aufgabe: Finde Position von B in A.
Naiv: Gehe durch A, vergleiche die k folgenden Einträge mit B
O(nk) Vergleiche
Lösung mit Hashing: Berechne Hash h(B) und vergleiche mit
h((ai , ai+1 , . . . , ai+k−1 )).
Vermeide die Neuberechnung von h((ai , ai+1 , . . . , ai+k−1 )) für
jedes i =⇒ O(n) erwartet
18
Sliding Window Hash
Mögliche Hash-Funktion: Summe aller Elemente:
Kann einfach aktualisiert werden: ai abziehen und ai+k hinzufügen.
Aber: schlechte Hash-Funktion
19
Sliding Window Hash
Mögliche Hash-Funktion: Summe aller Elemente:
Kann einfach aktualisiert werden: ai abziehen und ai+k hinzufügen.
Aber: schlechte Hash-Funktion
Besser:
P
k−j−1
Hc,m ((ai , · · · , ai+k−1 )) = k−1
mod m
j=0 ai+j c
c = 1021 Primzahl
m = 215 int, keine Überläufe bei Berechnungen
19
Sliding Window Hash
Mögliche Hash-Funktion: Summe aller Elemente:
Kann einfach aktualisiert werden: ai abziehen und ai+k hinzufügen.
Aber: schlechte Hash-Funktion
Besser:
P
k−j−1
Hc,m ((ai , · · · , ai+k−1 )) = k−1
mod m
j=0 ai+j c
c = 1021 Primzahl
m = 215 int, keine Überläufe bei Berechnungen
Hc,m ((ai+1 , · · · , ai+k )) = kj=1 ai+j ck−j mod m =
c · Hc,m ((ai , · · · , ai+k−1 )) + ai+k − ck ai mod m
P
19
Sliding Window Hash
template<typename It1, typename It2>
It1 findOccurrence(const It1 from, const It1 to,
const It2 begin, const It2 end)
{
const unsigned k = end - begin;
const unsigned M = 32768;
const unsigned C = 1021;
// your code here
// ...
20
Sliding Window Hash
// elements can be compared using std::equal:
if(std::equal(window_left, window_right, begin, end))
return current;
// if no element is found return end of array
return to;
}
21
Sliding Window Hash
Gehen Sie sicher, dass
der Algorithmus ck nur einmal berechnet,
alle Werte modulo m berechnet werden, um Überläufe zu
vermeiden (verwenden Sie die Rechenregeln für Kongruenzen),
und
alle Werte positiv sind (z.B. durch Addition von Vielfachen
von m).
22
Fragen?
23
Fragen?
Let’s get to work.
23
Herunterladen