Musterlösung

Werbung
Prof. Dr. Dennis Hofheinz
Lukas Barth, Lisa Kohl
K a r l s ru h e r I n s t i t u t f ü r T e c h n o l o g i e
Institut für Theoretische Informatik
12. Übungsblatt zu Algorithmen I im SoSe 2016
https://crypto.iti.kit.edu/index.php?id=algo-sose16
{lisa.kohl,lukas.barth}@kit.edu
Musterlösungen
Aufgabe 1
(Rucksackproblem, 2 + 2 Punkte)
Gegeben seien n = 5 Gegenstände mit Gewichten w1 = 40, w2 = 18, w3 = 20, w4 = 15, w5 = 10
und jeweiligen Profiten p1 = 90, p2 = 45, p3 = 52, p4 = 42, p5 = 35. Gesucht ist eine Teilmenge der
Gegenstände, die insgesamt das Gewicht M = 50 nicht überschreitet und die Summe der zugehörigen
Profite maximiert.
a) Finden Sie diese Teilmenge mit Hilfe dynamischer Programmierung. Verwenden Sie dazu wie in
der Vorlesung vorgestellt eine Tabelle (Vorlesung vom 06.07., Folie 13).
b) Finden Sie diese Teilmenge mit Hilfe von Branch-and-Bound. Verwenden Sie dazu die Schreibweise
aus der Vorlesung (Vorlesung vom 11.07., Folie 12). Sortieren Sie die Gegenstände zunächst gemäß
der Annahme im vorgestellten Algorithmus. Benutzen Sie als obere Schranke den maximalen Wert,
der erreichbar wäre, wenn auch Bruchteile von Gegenständen eingepackt werden dürften.
Musterlösung:
• Wir betrachten nur die Gewichte, die als Kombination auftreten können.
i\C
0
1
2
3
4
5
10
0
0, (0)
0, (0)
0, (0)
0, (0)
35, (1)
15
0
0, (0)
0, (0)
0, (0)
42, (1)
42, (0)
18
0
0, (0)
45, (1)
45, (0)
45, (0)
45, (0)
20
0
0, (0)
45, (1)
52, (1)
52, (0)
52, (0)
25
0
0, (0)
45, (1)
52, (1)
52, (0)
77, (1)
i\C
0
1
2
3
4
5
35
0
0, (0)
45, (1)
52, (1)
94, (1)
94, (0)
38
0
0, (0)
45, (1)
97, (1)
97, (0)
97, (0)
40
0
90, (1)
90, (0)
97, (1)
97, (0)
97, (0)
43
0
90, (1)
90, (0)
97, (1)
97, (0)
97, (0)
45
0
90, (1)
90, (0)
97, (1)
97, (0)
129, (1)
28
0
0, (0)
45, (1)
52, (1)
52, (0)
80, (1)
48
0
90, (1)
90, (0)
97, (1)
97, (0)
132, (1)
30
0
0, (0)
45, (1)
52, (1)
52, (0)
87, (1)
33
0
0, (0)
45, (1)
52, (1)
87, (1)
87, (0)
50
0
90, (1)
90, (0)
97, (1)
97, (0)
132, (1)
Damit liefert die Teilmenge {2, 3, 5} einen maximalen Profit von 132.
• Es gilt wp11 = 2, 25, wp22 = 2, 5, wp33 = 2, 6, wp44 = 2, 8 und wp55 = 3, 5, wir müssen die Objekte also
gerade in umgekehrter Reihenfolge betrachten. Der theoretisch maximal erreichbare Profit beträgt
5
p5 + p4 + p3 + 18
p2 = 141, 5. Wir schreiben C, wenn der Abzweig das Maximalgewicht überschreiten
würde und B, wenn der Abzweig maximal ein Gewicht liefern würde, das nicht größer als das
bisher entdeckte Optimalgewicht ist.
1
????? (141,5)
1???? (141,5)
B
11??? (141,5)
111?? (141,5)
C
Aufgabe 2
110?? (137,75)
1110? (140,25)
C
10??? (136,5)
11100 (129)
1101? (137,75)
C
B
101?? (136,5)
1100? (133,25)
C
B
1011? 136,5
C
B
B
10110 (132)
(Doktor Meta und dynamische Programmierung, 2 + 2 + 4 + 2 Punkte und 2 Bonuspunkte)
Dem ebenso beharrlichen wie größenwahnsinnigen Doktor Meta ist es endlich gelungen, er steht kurz vor
Übernahme der Weltherrschaft. Dies möchte er gebührend feiern und beauftragt dazu seinem ebenso
getreuen wie tollpatschigen Gehilfen Røbøt mit der Zusammenstellung einer Playlist. Damit diese seinen
Ansprüchen genügt und um schlechte Übergänge zu vermeiden, hat Doktor Meta eine Liste von n
Liedern zusammengestellt und darüber hinaus zu je zwei Liedern i und j eine Bewertung ci,j abgegeben,
die anzeigt, wie gut Lied j klingt, wenn es direkt nach Lied i abgespielt wird. Die Aufgabe von Røbøt
ist es nun eine Reihenfolge zu finden (wobei jedes Lied genau einmal gespielt werden soll), so dass die
Summe der Bewertungen ci,j für hintereinander gespielte Lieder maximiert wird. Helfen Sie Røbøt,
indem Sie berechnen, wie hoch diese Summe maximal sein kann. Zwei Bonuspunkte gibt es, wenn Sie
zusätzlich
die zugehörige Reihenfolge der Lieder zurückgeben. Der Algorithmus soll eine Laufzeit in
1
O n2 2n haben.
a) Zählen Sie die Tabelle(n) auf, die Ihr Algorithmus verwenden wird und erklären Sie die Bedeutung
der jeweiligen Einträge.
b) Geben Sie die Rekurrenz und den Basis-Fall an, den Ihr Algorithmus verwendet.
c) Geben Sie Ihren Algorithmus im Pseudo-Code an.
d) Analysieren Sie die Laufzeit Ihres Algorithmus. Begründen Sie ihre Antwort kurz.
Musterlösung:
a) Die zu Grunde liegende Tabelle besteht aus Einträgen der Form C[S, i], wobei S eine Teilmenge
der n Lieder ist und i ein Element aus S. C[S, i] entspricht der maximal möglichen Kompatibilität
für eine Liste der Lieder in Teilmenge S, die mit Lied i endet.
(
b) C[S, i] =
0
maxk∈S\{i} (C[S\{i}, k] + ck,i )
falls |S| = 1
falls|S| > 1
c) Ein möglicher Algorithmus basierend auf dieser Rekursion ist der Folgende:
Function songordering(n : N, c : Matrix of R): nat
C : Matrix of R
for j:= 1 to n do
C[{j}, j]:= 0
for j:= 2 to n do
foreach S with |S| = j do
foreach i ∈ S do
2
C[S, i]:= maxk∈S\{i} (C[S\{i}, k] + ck,i )
return max1≤i≤n C[{1, . . . , n}, i]
d) Die erste Schleife benötigt n Durchläufe. Das Finden des Maximums in der zweiten Schleife
benötigt Zeit O(|S|). Daher läuft die innerste Schleife „foreach i ∈ S“ insgesamt in Zeit O |S|2 ,
beziehungsweise O(ns ) für beliebiges S. Die äußeren beiden Schleifen iterieren über alle möglichen
Teilmengen von {1, ..., n} der Größe mindestens 2, diese Anzahl kann
nach oben mit 2n abgeschätzt
werden. Damit ergibt sich insgesamt eine Laufzeit von O 2n n2 .
Bonus) Um die Liste der Lieder auszugeben, speichert man in C[S, i] zusätzlich zum optimalen Wert auch
das Element k, für das dieser Wert erreicht wurde, also (für |S| > 1):
C[S, i] = ( max (C[S\{i}, k] + ck,i ), arg max(C[S\{i}, k] + ck,i ))
k∈S\{i}
k∈S\{i}
Algorithmisch lässt sich dies leicht in die vorletzte Zeile des Pseudocodes integrieren. Will man nun
die optimale Liste der Lieder abrufen, so schaut man zunächst ins Feld C[{1, . . . , n}, n] und sieht
dort nicht nur der optimal erreichbaren Wert, sondern auch das Lied, das in einer optimales Lösung
vor Lied n kommt. Sei dieses Lied k. Als nächstes schaut man in das Feld C[{1, . . . , n − 1}, k]
und findet dort das Lied, das in einer optimalen Lösung vor k kommt. Dies setzt man fort, bis
man die komplette Liste der Lieder (in umgekehrter Reihenfolge!) rekonstruiert hat.
Aufgabe 3
(Kickern, 1 + 2 + 4 Punkte und 2 + 2 Bonuspunkte)
In der Informatik-Fakultät wird begeistert gekickert, und so soll eine Fakultätsmeisterschaft ausgetragen
werden. Da Informatiker von Natur aus kompetitiv sind, soll nachher möglichst für jedes Paar von
Mitarbeitern der Fakultät feststehen, wer gegen wen gewonnen hat. Gleichzeitig haben die n Mitarbeiter
(hierzu zählen auch die Professoren) aber mit dem Betreuen von Vorlesungen so viel zu tun, dass nicht
daran zu denken ist, n2 Spiele auszutragen. Daher spielt immer nur jeder Mitarbeiter gegen seinen
unmittelbaren Vorgesetzten und umgekehrt, also z.B. jeder Professor gegen alle seine Mitarbeiter, usw.
Jeder Mitarbeiter außer dem Dekan hat genau einen Vorgesetzten. Für jedes ausgeführte Spiel werden
die Beteiligten sowie die erreichte Tordifferenz notiert. So bedeutet z.B. der Eintrag
(Bob, Alice, 5)
dass Bob gegen Alice mit 5 Toren Differenz gewonnen hat, während
(Eve, M allory, −3)
bedeutet, dass Eve gegen Mallory mit 3 Toren Differenz verloren hat.
Um auch für Informatiker, die nicht gegeneinander gespielt haben, festzustellen, wer gegen wen gewonnen
hat, wird nach folgenden zwei Regeln vorgegangen:
• Hat Spieler A gegenüber Spieler B eine Tordifferenz von x, Spieler B gegenüber Spieler C eine
Tordifferenz von y, und haben A and C nicht gegeneinander gespielt, so hat A gegenüber C die
Tordifferenz x + y
• Hat Spieler A gegenüber Spieler B eine positive Tordifferenz (d.h. größer als 0), so hat A gegen B
gewonnen.
Beispiel: Folgende Spiele haben stattgefunden:
(Bob, Alice, 5), (Eve, M allory, −3), (Alice, Eve, 1)
Damit hat Bob gegenüber Mallory eine Tordifferenz von 3 und somit gegen Mallory gewonnen.
Im Folgenden dürfen Sie davon ausgehen, dass alle Namen maximal 100 Zeichen lang sind, und die
maximale Tordifferenz einer Partie 10 beträgt.
3
a) Warmup: Folgende Spiele haben stattgefunden:
(Cormen, Garey, -6), (Garey, Johnson, -3), (Garey, Rivest, 1), (Floyd, Cormen, -4), (Floyd, Warshall,
2), (Cormen, Dijkstra, -2), (Bellman, Dijkstra, 4), (Bellman, Kruskal, 1), (Bellman, Jarnik, -2),
(Bellman, Prim, 1), (Ford, Dijkstra, -2), (Ford, Karatsuba, 4), (Ford, Offman, 3), (Offman, Euler,
-4), (Offman, Steiner, -3)
Nach der oben angegebenen Definition von gewonnen: Gegen wen hat Herr Garey gewonnen?
Gegen wen steht es unentschieden?
b) Begründen Sie kurz: Die Definition der Tordifferenz ist konsistent, insbesondere kann folgender
Fall nicht eintreten:
– A hat gegenüber B Tordifferenz x
– A hat gegenüber C Tordifferenz y
– B hat gegen D gespielt und Tordifferenz x0 gegenüber D
– C hat gegen D gespielt und Tordifferenz y 0 gegenüber D
– x + x0 6= y + y 0
c) Skizzieren Sie einen Algorithmus, der für eine gegebene Person A und eine Ergebnisliste in
O(n log n) Zeit berechnet, gegen wen A gewonnen hat. Ihr Algorithmus hat dabei ausschließlich
der Ergebnisliste als Eingabe zur Verfügung, und keine Zusatzinformationen wie z.B. die Liste
der Mitarbeiter oder die Hierarchie innerhalb der Informatik-Fakultät. Geben Sie an welche
Datenstrukturen Sie jeweils verwenden und beachten Sie die dafür anfallenden Laufzeiten.
d) Bonusaufgabe für 2 Bonuspunkte: Begründen Sie das Laufzeitverhalten Ihres Algorithmus.
e) Bonusaufgabe für 2 Bonuspunkte: Lässt sich das auch in erwartet O(n) berechnen? Oder sogar
deterministisch in O(n)? Wenn ja, skizzieren Sie jeweils, wie das geht. Wenn nein, begründen Sie
kurz.
Musterlösung:
a) Garey hat gewonnen gegen: Rivest, Cormen, Floyd, Warshall, Dijkstra, Kruskal, Prim, Ford,
Karatsuba, Offman, Euler und Steiner.
b) Aus der Aufgabenstellung, insbesondere aus der Bedingung, dass jeder nur gegen seinen direkten
Vorgesetzten spielt, ergibt sich, dass der Graph, in dem jede Person ein Knoten ist, und in dem
eine Kante existiert, wenn zwei Personen gegeneinander gespielt haben, ein Baum ist. In einem
Baum existiert zwischen zwei Knoten genau ein eindeutiger Pfad. Sei P = hA, v1 , v2 , . . . vk , Di der
Pfad von A nach D. Es ist klar, dass nur entweder vk = B, vk = C oder vk 6∈ {B, C} gelten kann.
O.b.d.A. nehmen wir an, dass vk 6= B. Da B ein Nachbar von D ist, muss der Pfad P 0 von A nach
B über D führen, und außerdem der einzige Pfad von A nach B sein. Nach Definition (und mit
den Symbolen aus der Aufgabenstellung) ist damit aber die Tordifferenz von A gegenüber B (also
x) festgelegt als die Tordifferenz von A gegenüber D plus die Tordifferenz von D gegenüber B.
Sei die Tordifferenz von A gegenüber D z = y + y 0 , dann gilt also x = z − x0 = y + y 0 − x0 und
damit x + x0 = y + y 0 .
c) Die Grundidee ist, dass wir den in Aufgabenteil b beschriebenen Graphen aufbauen. Es fällt
auf, dass die Tupel (Person1, Person2, Ergebnis) gerade genau zwei gerichtete Kanten (mit
dazugehörigem Gewicht) spezifizieren: Die Kante (P erson1, P erson2, Ergebnis) sowie die Kante
(P erson2, P erson1, −1 · Ergebnis). Damit können wir uns einfach eine Liste aller gerichteten
Kanten erstellen. Wir sortieren zunächst die Liste dieser Kanten nach dem ersten der beiden
Namen und wenden dann die aus der Vorlesung bekannte Technik (Foliensatz vom 13.6., Folie 18)
an, um daraus eine Adjazenzfelddarstellung des Graphen zu erhalten. Diese Darstellung erweitern
wir um ein Feld T [v] pro Knoten v, das mit 0 initialisiert wird, und das die Tordifferenz von
A gegenüber diesem Knoten speichern wird. Auf dem erstellten Graphen starten wir dann eine
4
Tiefensuche (oder Breitensuche) ausgehend von A. Sei c[(u, v)] das Kantengewicht der gerichteten
Kante (u, v). Die Tiefen- (oder Breiten-)suche wird dann beim Relaxieren jeder Kante (u, v)
T [v] = T [u] + c[(u, v)] setzen. Abschließend geben wir alle die Knoten v aus, für die T [v] > 0 gilt.
d) Um die Kanten- aus der Ergebnisliste zu erstellen, muss jedes Ergebnis einmal angeschaut und
zwei Einträge in eine verkettete Liste eingefügt werden. Dies benötigt insgesamt O(n) Zeit. Das
Sortieren der Kantenliste läuft in O(n log n) (oder, mit etwas Argumentation bezüglich endlicher
Alphabete und Namenslängen sogar in O(n). . . ). Die Tiefensuche benötigt erneut O(n) Zeit, und
abschließend müssen nur nocheinmal n Einträge in T angeschaut werden. Insgesamt ergibt sich
O(n log n) Zeitkomplexität.
e) Erwartet O(n): Sei h eine zufällig aus einer universellen Familie gezogene Hashfunktion. Wir
initialisieren zwei Hashtabellen mit je n Einträgen und einen Stack. Die erste Hashtabelle sei
die Punkte-Hashtabelle. In diese wird anfangs (A, 0) eingefügt. Die zweite Hashtabelle sei die
Ergebnis-Hashtabelle. In diese werden für jedes Ergebnis der Form (Person1, Person2, Ergebnis)
die Einträge (P erson1, (P erson2, Ergebnis)) und (P erson2, (P erson1, −1 · Ergebnis)) eingefügt.
Außerdem wird A auf den Stack gelegt. Danach geht man wie folgt vor: Entnehme das oberste
Element des Stacks (X) und ermittle seine Nachbarn, indem in der Ergebnis-Hashtabelle nach
dem Element gesucht wird. Für jeden Nachbarn (Y ) wird geschaut, ob schon Punkte für ihn in
der Punkte-Tabelle eingetragen sind. Wenn nicht, werden Punkte eingetragen, und zwar bekommt
Y die Punke von X plus des Ergebnisses zwischen X und Y . Danach wird Y auf den Stack gelegt.
Wiederhole, bis der Stack leer ist.
Alle Zugriffe auf die Hashtabellen (sowie auf den Stack) liegen dank universellem Hashing erwartet
in O(1). Im die Gesamtlaufzeit von erwartet O(n) zu sehen, genügt es zu zeigen, dass alle Elemente
nur konstant oft betrachtet werden müssen. Zunächst ist klar, dass jedes Element nur einmal in
den Stack eingefügt (und damit auch nur einmal herausgenommen) wird: Wird ein Element auf
den Stack gelegt, so werden auch Punkte für das Element eingetragen, und ein Element, für das
bereits Punkte eingetragen wurden, wird nie wieder auf den Stack gelegt. Etwas schwieriger ist es,
zu sehen, dass die Anzahl der „Nachbarn“, die betrachtet werden müssen, insgesamt auch nur
2(n − 1) ist: Jede „Schaue für einen Nachbarn, ob Punkte eingetragen sind“-Operation entspricht
der Traversierung einer Kante im Graphen von Aufgabenteil b. Von diesen Kanten gibt es n − 1
Stück, und jede Kante kann nur dann von a nach b traversiert werden, wenn a der gerade vom Stack
genommene Knoten ist. Da das pro Knoten nur ein einziges Mal passiert, wird diese Operation
nur insgesamt 2(n − 1) Mal ausgeführt.
Deterministisch O(n): In der Aufgabenstellung steht, dass alle Namen maximal 100 Zeichen
lang sind und die maximale Tordifferenz 10 beträgt. Da außerdem das Alphabet endlich ist, können
wir also auf die Ergebnis-Tupel auch ganzzahlige Sortierung (z.B. Bucketsort) anwenden. Damit
läuft der in Aufgabenteil c skizzierte Algorithmus in O(n).
5
Herunterladen