¨Ubungsblatt zur Vorlesung Datenstrukturen und Algorithmen SS

Werbung
Christian Scheideler
u. v. a.
Paderborn, 1. Juli 2011
Abgabe 8. Juli 2011, 11.15 Uhr
Kästen im D3-Flur
Übungsblatt zur Vorlesung
Datenstrukturen und Algorithmen
SS 2011
Blatt 12
Aufgabe 1: Gegeben sei eine leere Hashtabelle mit m = 13.
1. Nachfolgend betrachten wir Hashing mit verketteten Listen zur Kollisionsverwaltung.
Wählen Sie unter den unten aufgeführten Hashfunktionen eine gute aus und begründen
Sie ihre Wahl. Stellen Sie weiterhin die Hashtabelle nach dem Einfügen der Schlüssel
in der Reihenfolge 12, 23, 13, 56, 26, 45, 10 dar.
h11 (x) := (3x + 1) mod m
h12 (x) := ((2x + 7) mod 11) mod m
2. Nachfolgend betrachten wir Hashing mit offener Adressierung. Wählen Sie unter den
unten aufgeführten Hashfunktionen eine gute aus und begründen Sie ihre Wahl. Stellen
Sie weiterhin die Hashtabelle nach dem Einfügen der Schlüssel in der Reihenfolge 12,
23, 13, 56, 26, 45, 10 dar.
h21 (x, i) := (2 · h12 (x) + 26 · i) mod m
h22 (x, i) := (h11 (x) + i) mod m
Aufgabe 2: Gegeben sei ein Array A = (a1 , a2 , . . . , an ) mit ai ∈ Z \ {0} für 1 ≤ i ≤ n.
a) Entwickeln Sie einen Algorithmus, welcher mittels geeigneter Operationen das Array A
in ein Array ASort = (a01 , a02 , . . . a0j , . . . , a0n ) überführt, wobei für ein festes j gilt: a0i < 0
für alle 1 ≤ i ≤ j und a0i > 0 für alle j < i ≤ n. Die Laufzeit Ihres Algorithmus sollte
O(n) nicht überschreiten. Dabei darf der Algorithmus nur konstant viele zusätzliche
Speicherplätze verwenden. Geben Sie den Pseudocode Ihres Algorithmus an.
b) Zeigen Sie die Korrektheit und analysieren Sie das Laufzeitverhalten und den Speicherplatzbedarf Ihres Algorithmus.
Aufgabe 3: Beweisen Sie, dass es keinen Algorithmus geben kann, der die folgenden drei
Eigenschaften gleichzeitig erfüllt:
• Der Algorithmus benutzt wie ein Vergleichssortierer nur Vergleiche, Zuweisungen, usw.
• Der Algorithmus erstellt aus einem Array A einen binären Suchbaum mit den Elementen
aus A.
• Bei Eingabe eines Arrays A der Länge n hat der Algorithmus Laufzeit O(n).
Aufgabe 4: Gegeben sei eine Menge von n Messbechern M1 , . . . , Mn . Der Messbecher Mi
hat das Volumen Vi ∈ N Litern. Mit Hilfe der Messbecher sollen genau l ∈ N Liter Wasser
in einen Tank geschüttet werden. Dabei kann ein Messbecher immer nur komplett gefüllt
und in den Tank geschüttet werden. Aus dem Tank kann kein Wasser mehr entnommen
werden. Ein Messbecher kann natürlich mehrfach verwendet werden. Einer der Messbecher
hat ein Volumen von genau einem Liter. Gesucht ist ein Algorithmus, der liefert, wie viele
Schüttvorgänge mindestens benötigt werden. Beispiel: Für n = 3, V1 = 1, V2 = 20, V3 = 50
und l = 61 werden mindestens 4 Schüttvorgänge benötigt (l = V2 + V2 + V2 + V1 ).
1. Geben Sie die Rekursionsgleichung für OP T (i, j) an. OP T (i, j) ist die minimale Anzahl der Schüttvorgänge, um j Liter in den Tank zu füllen, wenn nur die Messbecher
M1 , . . . , Mi verwendet werden dürfen.
2. Geben Sie den Pseudocode eines Algorithmus an, der OP T (n, l) in Laufzeit O(n · l)
berechnet.
Aufgabe 5: Wir betrachten das Problem der Matrix-Ketten Multiplikation. Gegeben eine Sequenz hA1 , A2 , . . . , An i von n ∈ N Matrizen, ist es das Ziel möglichst effizient das
Produkt C = A1 A2 · · · An berechnen. Das Problem hierbei ist nicht, die einzelnen MatrixMutliplikationen effizient durchzufürhren, sondern zu entscheiden, in welcher Reihenfolge
wir die Matrixmultiplikationen durchzuführen, sodass die Anzahl der Skalarmultiplikationen
minimiert wird. Da die Matrixmultiplikation assoziativ ist, stehen uns hierfür viele Möglichkeiten zur Verfügung. D. h. unabhängig davon, wie wir das Produkt A1 A2 · · · An klammern,
bleibt das Ergebnis unverändert. Allerdings beeinflusst die Reihenfolge, in der wir das Produkt klammern, die Anzahl der Skalarmultiplikationen, die benötigt werden, um das Matrixprodukt zu berechnen. Zur Berechnung des Produktes zweier Matrizen verwenden wir im
Folgenden der Einfachheit halber den O(n3 )-Algorithmus MatrixMultiplikation (Foliensatz 18, Folie 11). Dieser benötigt für die Berechnung des Produktes einer p × q und einer
q × r Matrix p · q · r Skalarmultiplikationen.
Beispiel: Sei A eine 10 × 30 Matrix, B eine 30 × 5 Matrix und C eine 5 × 60 Matrix.
Dann benötigt die Berechnung des Produktes A · B · C mit der Klammerung A · (B · C)
gerade (30 · 5 · 60) + (10 · 30 · 60) = 27000 Operationen, mit der Klammerung (A · B) · C
allerdings nur (10 · 30 · 5) + (10 · 5 · 60) = 4500 Operationen.
Im obigen Beispiel ist somit die Berechnung des Produktes A · B · C mit der Klammerung
(A · B) · C effizienter als mit der Klammerung A · (B · C). Wie bestimmen wir nun aber eine
Klammerung, die die Anzahl der durchzuführenden Skalarmultiplikationen (Kosten) bei der
Berechnung des Produktes A1 A2 · · · An minimiert? Im Folgenden bezeichnen wir eine derartige Klammerung als optimale Klammerung.
Formal lässt sich das Problem der Matrix-Ketten Multiplikation somit nun wie folgt definieren: Gegeben eine Sequenz hA1 , . . . , An i von n Matrizen, wobei die Matrix Ai , i ∈ {1, . . . , n},
die Dimension pi−1 × pi hat, berechne eine Klammerung für das Produkt A1 A2 · · · An , sodass
die Anzahl der Skalarmultiplikationen minimiert wird.
Eine Möglichkeit zur Berechnung einer optimalen Klammerung ist, alle möglichen Klammerung zu überprüfen, was allerdings Zeit O(2n ) benötigt und somit i. A. sehr langsam ist.
Eine andere Möglichkeit zur Berechnung einer optimalen Klammerung ist, dieses Problem
mit Hilfe des Prinzips der dynamischen Programmierung zu lösen. Im Folgenden wird schrittweise beschrieben, wie wir das Problem auf diese Weise lösen.
Schritt 1: Struktur einer optimalen Klammerung
Im Folgenden bezeichne Ai...j , i ≤ j das Produkt von Ai Ai+1 · · · Aj . Für i < j muss jede
Klammerung von Ai Ai+1 · · · Aj , dieses Produkt zwischen Ak und Ak+1 , i ≤ k < j aufsplitten.
D. h. für einen Wert k berechnen wir zunächst die Matrizen Ai...k und Ak+1...j und multiplizieren diese anschließend miteinander, um das Produkt Ai...j zu erhalten. Die Kosten dieser
Klammerung sind somit gegeben durch die Kosten zur Berechnung von Ai...k , plus die Kosten
der Berechnung von Ak+1...j , plus die Kosten, diese beiden Matrizen miteinander zu multiplizieren.
Die optimale Teilstruktur dieses Problems ist somit wie folgt definiert: Angenommen eine optimale Klammerung von Ai Ai+1 · · · Aj teilt dieses Produkt zwischen Ak und Ak+1 auf. Dann
lässt sich zeigen, dass die Klammerung der Teilkette Ai Ai+1 · · · Ak innerhalb der optimalen
Klammerung von Ai Ai+1 · · · Aj ebenfalls optimal ist.
Eine optimale Klammerung für eine Sequenz von Matrizen können wir somit berechnen, indem wir das Problem in zwei Teilprobleme aufteilen, für die wir eine optimale Klammerung
finden und anschließend die optimalen Lösungen der Teilprobleme zu einer Gesamtlösung
zusammensetzen.
Schritt 2: Beschreibung einer rekursiven Lösung
Im Folgenden bezeichne m[i, j], 1 ≤ i ≤ j ≤ n, die minimale Anzahl an Skalaroperationen,
die benötigt wird, um Ai...j zu berechnen. m[1, n] enthält somit die minimalen Kosten, die
für die Berechnung des Produktes A1...n benötigt werden. Rekursiv lässt sich m[i, j] wie folgt
definieren:
(
0
if i = j
m[i, j] =
mini≤k<j {m[i, k] + m[k + 1, j] + pi−1 pk pj } if i < j
Um zusätzlich abzuspeichern, wie wir eine optimale Lösung konstruieren können, definieren
wir s[i, j], 1 ≤ i ≤ j ≤ n, als den Wert k, an dem wir das Produkt Ai Ai+1 . . . Aj aufteilen
müssen, um eine optimale Klammerung zu erhalten. D. h. s[i, j] entspricht einem Wert k für
den gilt: m[i, j] = m[i, k] + m[k + 1, j] + pi−1 pk pj .
Schritt 3: Berechnung der optimalen Kosten
Die Berechnung von m[i, j] für alle 1 ≤ i ≤ j ≤ n mittels der Rekursionsgleichung aus
Schritt 2 würde einen exponentiellen Zeitaufwand benötigen, was somit nicht besser wäre als
die brute-force Methode (alle Klammerungen durchzuprobieren).
Eine wichtige Beobachtung, die wir hierbei allerdings machen können, ist dass wir relativ
wenig Teilprobleme
haben: Ein Problem für jede Wahl von i und j mit 1 ≤ i ≤ j ≤ n, bzw.
n
insgesamt 2 + n = Θ(n2 ) Teilprobleme insgesamt. Anstatt die Lösungen für diese Teilprobleme, wie bei dem rekursiven Ansatz, immer wieder neu zu berechnen, speichern wir diese
in einer Tabelle ab und greifen lediglich drauf zu, wenn wir sie benötigen. Algorithmus 1
beschreibt eine Implementierung dieses Ansatzes.
Algorithm 1 Matrix-Chain-Order(p0 , . . . , pn )
1: for i ← 1 to n do
2:
m[i, i] ← 0
3: end for
4: for l ← 2 to n do
l bezeichnet die Kettenlänge
5:
for i ← 1 to n − l + 1 do
6:
j ←i+l−1
7:
m[i, j] ← ∞
8:
for k ← i to j − 1 do
9:
q ← m[i, k] + m[k + 1, j] + pi−1 pk pj
10:
if q < m[i, j] then
11:
m[i, j] ← q
12:
s[i, j] ← k
13:
end if
14:
end for
15:
end for
16: end for
17: return m and s
Ihre Aufgabe: Schreiben Sie ein Java-Programm, das folgende Aufgaben erfüllt:
1. Das Programm erwartet als Parameter den Dateinamen einer Textdatei, welche eine
Folge von n + 1 natürlichen Zahlen p0 p1 . . . pn enthält und ließt diese ein.
2. Berechnen Sie mittels des oben angegebenen Algorithmus’ Matrix-Chain-Order die
Werte m[i, j] für alle 1 ≤ i ≤ j ≤ n. Ihr Programm soll dabei nach den unten definierten
Ausgabespezifikationen die Werte m[i, j] für alle 1 ≤ i ≤ j ≤ n ausgeben.
Bitt beachten Sie zusätzlich folgende Punkte:
• Halten Sie die unten angegebenen Ein- und Ausgabespezifikationen bitte genau ein.
Das bedeutet insbesondere:
– Die Eingabedatei (bzw. der Pfad zu dieser inkl. des Dateinamens) soll als Eingabeparameter an das Programm übergeben werden. D. h. beispielsweise, dass die
Eingabedatei nicht immer inputfile.txt heißen muss, die Eingabedatei nicht im
gleichen Verzeichnis wie das Programm liegen muss und dass die Eingabe eines
Dateinamens nicht über eine Eingabeaufforderung geschehen soll.
– Es darf nur (und muss) genau das ausgegeben werden, was in der Ausgabespezifikation weiter unten definiert ist. D. h. es dürfen keine zusätzlichen Ausgaben
erfolgen und jede Zeile beginnt mit i : “ , wobei i die aktuelle Zeilennummer
”
bezeichnet.
• Ihr Programm muss in Java (Version ≤ 1.6) geschrieben sein.
• Ihr Programm darf nur aus einer einzigen (Java-)Quelldatei bestehen.
• Die Quelldatei Ihres Programmes muss Blatt12.java heißen. Dabei ist auf Groß- und
Kleinschreibung zu achten. D. h. blatt12.java ist beispielsweise nicht korrekt.
• Ihr Programm muss mit dem Befehl javac Blatt12.java auf einem Linux-Rechner
der Uni kompilierbar und mit dem Befehl java Blatt12 ausführbar sein.
• Die Abgabe dieser Aufgabe geschieht in digitaler Form. Hierfür laden Sie Ihr Programm
bitte auf der Seite http://funalg.cs.upb.de bis zum 8. Juli 2011, 11.15 Uhr hoch.
• Sie dürfen Ihr Programm in Gruppen von bis zu vier Personen abgeben. Geben mehrere
Gruppen das selbe Programm ab, so erhalten die betroffenen Gruppen für diese Aufgabe
0 Punkte.
• Beispielinstanzen zum Testen Ihres Programmes inkl. korrekter Ausgaben finden Sie
unter http://funalg.cs.upb.de/dua/blatt12.zip.
Bei Missachtung dieser Punkte wird Ihr Programm nicht ausgewertet!
Eingabespezifikation: Die einzulesende Datei besteht aus genau einer Zeile, welche eine
Folge p0 p1 . . . pn von n + 1 natürlichen Zahlen enthält. Die Zahlen sind jeweils durch ein
Leerzeichen voneinander getrennt. Die Zeile endet mit einem abschließenden Leerzeichen.
Die pi definieren dabei die Dimensionen von n Matrizen A1 , . . . , An . Die Matrizen selbst werden nicht benötigt und sind daher auch nicht gegeben. Die Matrix Ai habe die Dimension
pi−1 × pi , 1 ≤ i ≤ n.
Ausgabespezifikation: Ihr Programm soll n+1 Zeilen ausgeben (wobei n+1 die Anzahl der
eingelesenen Zahlen bezeichnet). Die i-te Zeile, 1 ≤ i ≤ n, beginne dabei mit i : und enthalte durch Leerzeichen voneinander getrennt die Werte, die in m[i, i], m[i, i + 1], . . . , m[i, n]
gespeichert sind. Die (n + 1)-te Zeile enthalte die Anzahl der Skalarmultiplikationen, die bei
der berechneten Klammerung zur Berechnung des Matrixproduktes von Matrizen mit den
angegebenen Dimensionen benötigt werden.
D. h. die Ausgabe Ihres Programmes soll wie folgt aussehen:
1 : m[1,1]
2 : m[2,2]
3 : m[3,3]
.
.
.
i : m[i,i]
.
.
.
n : m[n,n]
Kosten
m[1,2] m[1,3] ... m[1,n]
m[2,3] m[2,4] ... m[2,n]
m[3,4] m[3,5] ... m[3,n]
m[i,i+1] m[i,i+2] ... m[i,n]
Beispiel: Angenommen die einzulesende Textdatei mit dem Namen inputfile.txt enthält
folgenden Inhalt:
6 2 8 9 10 4 5 2 4
Nach dem Kompilieren mit dem Befehel javac Blatt12.java soll Ihr Programm durch
Aufruf von java Blatt12 inputfile.txt folgende Ausgabe liefern:
1 :
2 :
3 :
4 :
5 :
6 :
7 :
8 :
524
0
0
0
0
0
0
0
0
96 252 444 452 504 484 524
144 324 404 444 460 476
720 648 808 444 508
360 540 300 372
200 120 200
40 72
40
Herunterladen