Übungsblatt 5 - Institut für Informatik - Hu

Werbung
Humboldt-Universität zu Berlin
Institut für Informatik
Prof. Dr. Ulf Leser
M. Bux, B. Grußien, J. Sürmeli, S. Wandelt
Berlin, den 15.06.2015
Übungen zur Vorlesung
Algorithmen und Datenstrukturen
Übungsblatt 5
Abgabe: Montag den 29.6.2015 bis 11:10 Uhr vor der Vorlesung im Hörsaal oder bis 10:45 Uhr
in den Fächern im Raum RUD25 4.402. Die Übungsblätter sind in Gruppen von 2 Personen zu
bearbeiten. Sie können auf diesem Übungsblatt bis zu 50 Punkte erhalten. Zur Erinnerung:
Jedes Übungsblatt muss bearbeitet werden. Sie müssen mindestens ein Blatt für wenigstens eine Aufgabe jedes Übungsblattes abgeben. Die Lösungen sind auf nach Aufgaben
getrennten Blättern abzugeben. Vermerken Sie auf allen Abgaben Ihre Namen, Ihre
Matrikelnummern, den Namen Ihrer Goya-Gruppe und welchen Übungstermin bei welchem
Übungsleiter Sie besuchen. Heften Sie bitte die zu einer Aufgabe gehörenden Blätter
vor der Abgabe zusammen.
Beachten Sie auch die aktuellen Hinweise auf der Übungswebsite unter:
https://u.hu-berlin.de/alg_ds_ss15_u
Konventionen:
• Für ein Array A ist |A| die Länge von A, also die Anzahl der Elemente in A. Die
Indizierung aller Arrays auf diesem Blatt beginnt bei 1 (und endet also bei |A|). Bitte
beginnen Sie die Indizierung der Arrays in Ihren Lösungen auch bei 1.
• Da beim Hashing überwiegend mit modulo-Werten gerechnet wird, wird beim Hashing
die Indizierung aller Arrays A mit 0 beginnen und bei |A| − 1 enden. Bitte beachten
Sie dies bei Aufgaben 1 und 2.
• Mit der Aufforderung “Analysieren Sie die Laufzeit” ist hier gemeint, dass Sie eine
möglichst gute obere Schranke der Zeitkomplexität angeben sollen und diese begründen
sollen.
• Ein Stern markiert eine tendenziell schwierigere Aufgabe.
Aufgabe 1 (Hashing-Schreibtischtest)
2 + 2 + 3 + 3 + 2 = 12 Punkte
Gegeben sei eine Hashtabelle mit 11 Feldern für Einträge (Index 0 bis 10, siehe Konventionen)
und eine Hashfunktion h(x) = x mod 11. Führen Sie für die folgenden Hashverfahren einen
Schreibtischtest durch, indem Sie jeweils die Werte 19, 2, 14, 41, 33, 47 und 12 in dieser Reihenfolge in eine leere Tabelle einfügen und diese nach jeder Einfügeoperation ausgeben.
a) Hashing mit direkter Listenverkettung
Hierbei ist die Hashtabelle als Array von einfach verketteten Listen realisiert.
b) Offenes Hashing mit linearem Sondieren
Bei Kollisionen wird hier das einzufügende Element an der nächsten freien Stelle links vom
berechneten Hashwert eingefügt. Das heißt, die Sondierungsreihenfolge (die Reihenfolge,
in der die Plätze im Array durchgegangen werden, bis erstmals ein freier Platz angetroffen
wird) ist gegeben durch s(x, i) = (x − i) mod 11 für i = 0, . . . , 10.
c) Doppeltes Hashing
Das doppelte Hashing ist ein offenes Hashing, bei dem die Sondierungsreihenfolge von einer
zweiten Hashfunktion h0 (x) = 1 + (x mod 7) abhängt. Die Position für das i-te Sondieren
ist bestimmt durch die Funktion s(x, i) = (h(x) − i · h0 (x)) mod 11 für i = 0, . . . , 10.
d) Geordnetes Hashing
Das geordnete Hashing ist ein offenes Hashing, bei dem die gemäß Sondierungsreihenfolge
angetroffenen Elemente (hier absteigend) sortiert werden. Die Position eines neu einzufügenden Elements x ist die erste Position gemäß der Sondierungereihenfolge, an der ein
kleineres Element oder noch kein Element im Array steht. Wenn auf ein kleines Element y
getroffen wird, wird dieses durch das neue Element x ersetzt. Danach wird das Element y
neu nach diesem Verfahren eingefügt, und das Ganze rekursiv fortgesetzt. Beim geordneten Hashing werden stets Sondierungsververfahren benutzt, bei denen sich für das Element
y anhand seiner Position direkt die Folgepositionen innerhalb der Sondierungsreihenfolge
bestimmen lassen.
Zum Sondieren nutzen Sie das doppelte Hashing mit der in Aufgabenteil c) gegebenen
Sondierungsfunktion.
e) Uniformes offenes Hashing
Hier erhält jeder Schlüssel mit gleicher Wahrscheinlichkeit eine der 11! Permutationen von
{0, 1, . . . , 10} als Sondierungsreihenfolge zugeordnet.
Als „zufällige“ Permutationen nutzen Sie:
L(19) = 2, 10, 9, 3, 0, 8, 7, 6, 1, 4, 5
L(2) = 1, 5, 9, 3, 7, 10, 4, 8, 6, 2, 0
L(14) = 0, 8, 5, 2, 1, 10, 7, 9, 6, 3, 4
L(41) = 1, 4, 10, 5, 2, 0, 7, 3, 6, 9, 8
L(33) = 6, 8, 4, 0, 9, 1, 5, 2, 10, 3, 7
L(47) = 4, 2, 3, 5, 1, 8, 9, 0, 7, 6, 10
L(12) = 1, 3, 6, 0, 4, 5, 2, 10, 8, 7, 9
Mehr zum uniformen offenen Hashing finden Sie auch in Aufgabe 2.
Aufgabe 2 (Uniformes offenes Hashing)
2 + 4 + 2 + 2 + 3 + 3 = 16 Punkte
Wir betrachten in dieser Aufgabe den Idealfall für offene Hashverfahren. Dieser Fall liegt vor,
wenn die Sondierungsfolge für einen Schlüsselwert gleichverteilt (d.h. uniform) aus allen möglichen Permutationen der Hashtabellenpositionen gezogen wird.
Sei T eine Hashtabelle mit m Slots mit den Indizes 0, 1, . . . , m − 1. Sei P erm(m) die Menge
aller m! Permutationen der Elemente 0, 1, 2, . . . , m − 1, d. h., P erm(m) enthält m! viele unterschiedliche Listen L, wobei jedes L eine Reihenfolge der Indizes 0, 1, . . . , m − 1 ist, |L| = m gilt,
und die Elemente in L paarweise verschieden sind. Weiterhin sei U die Menge der möglichen
Schlüsselwerte und es sei L : U → P erm(m) eine Funktion, die jedem Schlüssel k ∈ U uniform
zufällig eine Liste L(k) ∈ P erm(m) zuweist. Für eine gegebene Liste L ∈ P erm(m) und einen
1
Schlüsselwert k ∈ U ist die Wahrscheinlichkeit also m!
, dass L(k) = L gilt. Sei L(k)[i] das i-te
Element der Liste L(k) beginnend bei i = 0. Die Sondierungsreihenfolge für einen Schlüsselwert
k ∈ U ist durch die Funktion s(k, i) = L(k)[i] gegeben, d. h., beim i-ten Versuch wird das i-te
Element in L als Position in der Hashtabelle T ausgewählt. Wir nehmen an, dass sich genau
n < m Elemente in der Hashtabelle T befinden, somit beträgt der Loadfaktor von T genau
n
α= m
und es muss mindestens einen freien Slot in T geben.
Bei der Suche nach einem Schlüssel k in der Hashtabelle T (siehe Algorithmus search(T, k))
wird nun anhand der zugehörigen Liste L(k) die Hashtabelle durchsucht, bis k gefunden oder
auf einen leeren Slot von T zugegriffen wird.
search(T, k)
Input: Hashtabelle T und Schlüsselwert k
Output: True, falls k ∈ T ; False, sonst
for i = 0 to m − 1 do
if T [L(k)[i]] = k then
return True;
else if T [L(k)[i]] = null then
return False;
end if
end for
a) Sei Ta die folgende Hashtabelle:
0 1 2 3 4 5 6 7 8
– 2 – 12 47 – 5 – 44
9
–
10
41
Bestimmen Sie die Ausgabe von search(Ta , 41) und search(Ta , 33). Benutzen Sie dazu die
Sondierungsreihenfolgen aus Aufgabe 1e). Geben Sie zudem für beide Suchen den Wert
von Ta [L(k)[i]] bei jedem Durchlauf der for-Schleife an.
Wir betrachten im Folgenden eine einmalige erfolglose Suche nach einem Schlüssel k in der
oben allgemein beschriebenen Hashtabelle T der Größe m, in der bereits n Einträge belegt sind.
Seien pi die Wahrscheinlichkeit, dass bei search(T, k) genau i-mal auf einen belegten Slot in der
Tabelle zugegriffen wird, und qi die Wahrscheinlichkeit, dass bei search(T, k) mindestens i-mal
auf einen belegten Slot der Tabelle zugegriffen wird.
b) Bestimmen Sie (in Abhängigkeit von m und n) die Wahrscheinlichkeiten q1 , q2 , q3 und
folgern Sie daraus qi für 1 ≤ i ≤ n.
c) Bestimmen Sie qi für i > n.
d) Zeigen Sie für alle i ≥ 1, dass die Ungleichung qi ≤ αi gilt.
e) Zeigen Sie, dass die Gleichung
P∞
i=1 i
· pi =
P∞
i=1 qi
gilt.
Hinweis: Wie lässt sich qi , die Wahrscheinlichkeit, dass mindestens i-mal auf einen belegten
Slot zugegriffen wird, durch Wahrscheinlichkeiten für genaue Zugriffzahlen ausdrücken?
f) Wir interessieren uns für die erwartete Anzahl von Tabellenzugriffen bei einer erfolglosen
Suche nach Schlüssel k ∈
/ T . Damit search(T, k) nach i + 1 Zugriffen auf T erfolglos
abbricht, muss vorher i-mal auf einen belegten Slot und dann einmal auf einen freien
Slot zugegriffen werden. Sei E der Erwartungswert für die Anzahl der Zugriffe auf einen
belegten Slot bei der Suche nach k. Erwartungswert E lautet
E=
n
X
i · pi =
i=0
∞
X
i · pi ,
i=0
wobei die letzte Gleichung gilt, da pi = 0 für i > n. Der gesuchte Erwartungswert ist somit
1+E=1+
∞
X
i · pi .
i=0
Zeigen Sie, dass 1 + E ≤
1
1−α
gilt.
Hinweis: Verwenden Sie Aufgabenteile d) und e) und Ihr Wissen über geometrische Reihen.
Aufgabe 3 (Heaps, binäre Suchbäume)
3+4+2+2 = 11 Punkte
Zur Erinnerung: Der Median einer aufsteigend sortierten n-elementigen Folge a1 , a2 . . . , an ist
das Element ab n2 c+1 .
a) Betrachten Sie den folgenden Algorithmus:
Median_via_Heap(Array A)
Input: Array A mit n Elementen
Output: Medianelement aus A
1: B := fast_build_heap(A);
2: i := b n
2 c + 1;
3: C := [B[i], B[i + 1], . . . , B[n]];
4: D:= fast_build_heap(C);
5: return extract_max(D);
# d. h. B ist max-heapgeordnetes Array A
# d.h. C ist Array aller Blattelemente des Max-Heaps B
# d. h. D ist max-heapgeordnetes Array C
Der Pseudocode von fast_build_heap und extract_max ist auf Übungsblatt 4 zu finden.
Zeigen Sie, dass der obige Algorithmus zur Bestimmung des Medians nicht korrekt ist.
b) Nehmen Sie nun an, dass Sie einen binären Suchbaum B mit Wurzel r gegeben haben,
der die Schlüssel b1 < . . . < bn enthält. Für jeden Knoten k von B sei k.left der linke
Kindsknoten von k, falls dieser existiert, und null, falls k keinen linken Kindsknoten hat.
Analog sei k.right definiert. Außerdem sei jeder Knoten k mit der Anzahl k.size der
Knoten in dem durch k induzierten Teilbaum beschriftet. (Der durch k induzierte Teilbaum
ist der größtmögliche Teilbaum mit Wurzel k.) Zum Beispiel ist für die Wurzel r von B
also r.size die Anzahl der Knoten in B. Für jedes Blatt k gilt k.size = 1. Für jeden inneren
Knoten k, mit k.left 6= null und k.right 6= null gilt k.size = k.left.size+k.right.size+1.
i) Entwerfen Sie einen Algorithmus getElement(r, i), der als Eingabe die Wurzel r
eines Baumes B und eine Zahl i mit 1 ≤ i ≤ r.size erhält, und den Knoten mit
Schlüssel bi zurückgibt.
ii) Analysieren Sie die Laufzeit Ihres Algorithmus in Abhängigkeit von r.size für beliebige Eingaben r und i mit 1 ≤ i ≤ r.size.
iii) Wann ist der Schlüssel der Wurzel von B der Median von b1 , . . . , bn ? Geben Sie ein
notwendiges und hinreichendes Kriterium an.
Aufgabe 4 (Binäre Suchbäume)
2 + 3 + 3 +3 = 11 Punkte
In der Vorlesung haben Sie binäre Suchbäume kennengelernt, die ganze Zahlen als Schlüssel
speichern und die Operationen Einfügen, Suchen und Löschen von Schlüsseln unterstützen. Wir
betrachten hier nur Suchbäume, die keine Duplikate enthalten, d. h., alle Schlüssel des Baumes
sind paarweise verschieden.
a) Zeigen Sie: Wenn ein Knoten eines binären Suchbaumes zwei Kinder hat, dann hat sein
Nachfolger, d. h. der Knoten im Baum mit dem nächstgrößeren Schlüssel, kein linkes Kind.
b) Beweisen oder widerlegen Sie folgende Aussage: Wenn man aus einem binären Suchbaum
zwei verschiedene Elemente x und y löscht, so erhält man unabhängig von der Löschreihenfolge stets den gleichen Suchbaum. Wenden Sie dabei die Symmetric Predecessor-Methode
an, d.h. beim Löschen eines Knotens v mit zwei Kindern wird der Knoten v durch den
größten Knoten im linken Teilbaum ersetzt (siehe Vorlesung).
c) Zeigen Sie, dass es zu jedem binären Suchbaum T eine Reihenfolge seiner Elemente gibt,
so dass man durch Einfügen der Elemente in dieser Reihenfolge in einen anfangs leeren
Baum den Baum T erhält.
d) Angenommen Sie suchen einen Schlüssel k in einem binären Suchbaum und finden ihn
schließlich. Seien P die Schlüssel auf dem zugehörigen Suchpfad und L bzw. R die Schlüssel
in Teilbäumen, die links bzw. rechts vom Pfad P liegen (d. h., L, R und P sind paarweise
disjunkt).
Beweisen oder widerlegen Sie, dass dann für jede Wahl von x ∈ L, y ∈ P und z ∈ R die
Aussage x ≤ y ≤ z gilt.
Herunterladen