Datenstrukturen und Algorithmen Musterlösung zu Hausübungsblatt 2 Daniel Warner∗ 28. Mai 2007 Aufgabe 5 Nachfolgend ist der Algorithmus Merge-Sort im Pseudocode dargestellt [2]. Merge-Sort(A, p, r) 1 2 3 4 5 if p < r then q ← b (p+r) 2 c Merge-Sort(A,p,q) Merge-Sort(A,q + 1,r) Merge(A,p,q,r) Es sei A ein beliebiges Array der Länge n = r − p + 1. Der erste rekursive Aufruf von Merge-Sort operiert auf einem Array der Länge p+r c−p+1 2 p+r =b − p + 1c 2 r−p+1+1 =b c 2 n+1 =b c 2 n = d e. 2 q−p+1 = b Der zweite rekursive Aufruf von Merge-Sort operiert auf einem Array der Länge n n n − (q − p + 1) = n − d e = b c. 2 2 ∗ [email protected] Damit ergibt sich, mit dem Wissen über die worst-case-Laufzeit von Merge, die folgende Rekurrenzgleichung für die worst-case-Laufzeit von Merge-Sort für beliebiges n ∈ N, n > 2: n n T (n) = T +T + cn 2 2 Es gelte außerdem T (1) = 1. Wir vermuten T (n) 6 dn log n = O(n log n) und beweisen unsere Vermutung durch vollständige Induktion nach n. Dabei ist d ∈ R>0 eine geeignete Konstante, die erst im Laufe des Beweises konkret bestimmt wird. Zur Induktionsvoraussetzung: Es sei n > 2 und T (n 0 ) 6 dn 0 log n 0 gelte für alle n 0 < n. Zum Induktionsschritt: n n T (n) = T +T + cn 2 2 n n n n 6 d log +d log + cn 2 2 2 2 (I.V.) n n n log + + cn 6d 2 2 2 n = dn log + cn 2 n+1 < dn log + cn 2 3 6 dn log n + cn 4 (n>2) 3 = dn log + log n + cn 4 3 = dn log n + c + d log n 4 6 dn log n Die letzte Ungleichung ist erfüllt, falls d > c . log 43 Damit ist der Induktionsschritt gezeigt. Es fehlt nun noch der Induktionsanfang: Aufgrund der Definition des O-Kalküls genügt es den Induktionsanfang für n = 2 und n = 3 zu zeigen. Für n = 2 erhalten wir 2 2 ! T (2) = T +T + 2c = 2T (1) + 2c = 2c + 2 6 2d log 2 = 2d, 2 2 und für n = 3 erhalten wir 3 3 ! +T + 3c = T (2) + T (1) + 3c = 5c + 3 6 3d log 3. T (3) = T 2 2 Die beiden mit ! gekennzeichneten Ungleichungen gelten, falls d > c + 1 und d > also setzen wir d auf das Maximum von c + 1, 5c+3 3 log 3 und vollständig, und es gilt c . log 43 5c+3 3 log 3 , Damit ist der Beweis T (n) 6 dn log n = O(n log n). Es gilt bekanntlich sogar T (n) = Θ(n log n). Um dies zu beweisen, zeigt man analog mit der Substitutionsmethode, dass auch die untere Schranke T (n) = Ω(n log n) gilt. 2 Aufgabe 6 Wir lösen die durch T (n) = 3T ( n 2 ) + cn, falls n > 1 sonst d, definierte Rekurrenzgleichung durch Anwenden der Iterationsmethode. Dazu setzen wir T (n) iterativ in sich selbst ein und versuchen nach einigen Iterationen eine allgemeine Formel für die k.te Iteration aufzustellen: 1. Iteration T (n) = 3T ( n 2 ) + cn 2. Iteration = 3 3T ( 2n2 ) + c n 2 + cn = 32 T ( 2n2 ) + 3c n 2 +cn n 2 3. Iteration = 3 3T ( 23 ) + c 2n2 + 3c n 2 + cn = 33 T ( 2n3 ) + 32 c 2n2 + 3c n 2 + cn .. . Nach diesen Iterationsschritten haben wir Anlass zur Behauptung, dass T (n) = 3k T n k−1 X n + 3i c i k 2 2 i=0 für alle k ∈ N mit k 6 log n gilt.1 Wir beweisen die Korrektheit der geschlossenen Formel für T (n) durch vollständige Induktion: Der Induktionsanfang entspricht der 1. Iteration. Die Behauptung sei nun korrekt für k − 1 ∈ N mit k 6 log n. Dann gilt: T (n) = 3k−1 T (I.V.) k−1 =3 = 3k T = 3k T n k−2 X n + 3i c i k−1 2 2 i=0 n n X i n 3T k + c k−1 + 3c i 2 2 2 k−2 n n + 3k−1 c k−1 + 2k 2 n + 2k k−1 X i=0 3i c i=0 k−2 X 3i c i=0 n 2i n 2i Damit ist auch der Induktionsschritt gezeigt, und mit dem Prinzip der vollständigen Induktion folgt die Behauptung. Die Iteration bricht bei T (1) ab, d.h. wenn 2nk = 1 gilt. Das gilt aber genau dann, wenn k = log n. Wir formen nun die geschlossene Formel für T (n) unter Verwendung der 1 Wir nehmen ohne Einschränkung an, dass n eine Zweierpotenz ist. 3 endlichen geometrischen Reihe für k = log n um: T (n) = 3k T n k−1 X n + 3i c i k 2 2 i=0 X log(n)−1 = 3log n T (1) + 3i c i=0 = dnlog 3 + cn = = X log(n)−1 n 2i 3 i 2 i=0 3 log n −1 log 3 2 dn + cn 3 2 −1 3 nlog 2 − 1 log 3 dn + cn 1 2 log 3 log 32 = dn + 2cnn − 2cn = dnlog 3 + 2cn log( 32 )+1 = dnlog 3 + 2cn log( 32 )+log 2 = dn log 3 + 2cn log 3 − 2cn − 2cn − 2cn = (2c + d)nlog 3 − 2cn = Θ(nlog2 3 ) Wir erhalten also insgesamt T (n) = Θ(nlog2 3 ). Wir zeigen nun die Korrektheit des Algorithmus. Es sei n = 2k . Wir führen Induktion nach k. Der Induktionsanfang k = 0 ist klar: Der Algorithmus multipliziert zwei Bits stets korrekt. Als Induktionsvoraussetzung nehmen wir an, dass der Algorithmus auf allen Integern der Länge 2k−1 = n 2 korrekt arbeitet. Zum Induktionsschritt: Der Algorithmus erhält als Eingabe zwei n-Bit Integer X und Y. Diese werden in einen höherwertigen und einen n n 2 2 niederwertigen Teil der Länge jeweils n 2 aufgeteilt, sodass X = 2 A + B und Y = 2 C + D. Nach Induktionsvoraussetzung werden die Produkte BD, AC und (A + B)(C + D) korrekt berechnet. Einfaches Nachrechnen zeigt, dass n n n BD + 2 2 ((A + B)(C + D) − AC − BD) + 2n AC = (2 2 A + B)(2 2 C + D) = XY gilt, womit auch der Induktionsschritt gezeigt ist. Damit folgt die Korrektheit des Algorithmus. Aufgabe 7a Wir lösen die durch T (n) 3 6 9T ( n 3 ) + 9n , falls n > 1 sonst = c, 4 definierte Rekurrenzgleichung durch Anwenden der Iterationsmethode. Dazu setzen wir T (n) iterativ in sich selbst ein und versuchen nach einigen Iterationen eine allgemeine Formel für die k.te Iteration aufzustellen: 3 1. Iteration T (n) 6 9T ( n 3 ) + 9n 3 + 9n3 2. Iteration 6 9 9T ( 3n2 ) + 9( n ) 3 2 = 92 T ( 3n2 ) + 933 n3 + 9n3 2 3. Iteration 6 92 9T ( 3n3 ) + 9( 3n2 )3 + 393 n3 + 9n3 3 2 = 93 T ( 3n3 ) + (393 )2 n3 + 933 n3 + 9n3 .. . Nach diesen Iterationsschritten haben wir Anlass zur Behauptung, dass k n X 9i T (n) 6 9 T k + n3 3(i−1) 3 3 k i=1 für alle k ∈ N mit k 6 log3 n gilt.2 Wir beweisen die Korrektheit der geschlossenen Formel für T (n) durch vollständige Induktion: Der Induktionsanfang entspricht der 1. Iteration. Die Behauptung sei nun korrekt für k − 1 ∈ N mit k 6 log3 n. Dann gilt: T (n) 6 9 (I.V.) k−1 n k−1 X 9i n3 T k−1 + 3(i−1) 3 3 i=1 k−1 n n 3 X 9i 6 9k−1 9T k + 9 k−1 n3 + 3 3 33(i−1) i=1 n n 3 k−1 X 9i k k = 9 T k +9 n3 + 3 3k−1 33(i−1) i=1 k n X 9i k =9 T k + n3 3 33(i−1) i=1 Damit ist auch der Induktionsschritt gezeigt, und mit dem Prinzip der vollständigen Induktion folgt die Behauptung. Die Iteration bricht bei T (1) ab, d.h. wenn 3nk = 1 gilt. Das gilt aber genau dann, wenn k = log3 n. Wir formen nun die geschlossene Formel für T (n) unter Verwendung der 2 Wir nehmen ohne Einschränkung an, dass n eine Dreierpotenz ist. 5 unendlichen geometrischen Reihe für k = log3 n um: log3 n log3 n T (n) 6 9 T (1) + = cnlog3 9 + 9n3 X 9i 33(i−1) i=1 log3 n X i=1 6 cn2 + 9n3 = cn2 + 9 i−1 33 ∞ X 1 i 3 i=0 = cn2 + 9n3 n3 1 1− 1 3 27 3 n 2 = O(n3 ) Wir erhalten also insgesamt T (n) = O(n3 ). Aufgabe 7b Die Laufzeit T (n) eines Algorithmus A sei gegeben durch die Rekurrenzgleichung3 2 7T ( n 2 ) + n , falls n > 1 T (n) = 1, sonst, und die Laufzeit S(n) eines Algorithmus B sei gegeben durch die Rekurrenzgleichung 2 aS( n 4 ) + n , falls n > 1 S(n) = 1, sonst mit a ∈ N. Wir zeigen zwei unterschiedliche Herangehensweisen zur Lösung der Aufgabenstellung: Bei der ersten Variante gehen wir elementar vor, bei der zweiten Variante wenden wir das Master-Theorem an. Es zeigt sich im Vergleich der beiden Ansätze deutlich, dass die Verwendung des Master-Theorems einem in dieser Situation viel Arbeit abnimmt (die dafür aber in den Beweis des Master-Theorems investiert wurde). Bei beiden Beweisvarianten benutzen wir das folgende Lemma: Lemma 1. Es seien s1 : N → N und s2 : N → N Funktionen, und es gelte s1 ∈ o(s2 ). Dann gilt O(s1 ) ⊆ o(s2 ). 3 Dies ist gerade die Rekurrenzgleichung, die sich bei der Laufzeitanalyse des Matrixmultiplikationsalgorithmus von Strassen ergibt. 6 Beweis. Nach Voraussetzung gilt ∀c 0 > 0 ∃n0 ∈ N ∀n > n0 : s1 (n) 6 c 0 s2 (n). Es sei nun f ∈ O(s1 ) beliebig. Dann gilt ∃c1 > 0 ∃n1 ∈ N ∀n > n1 : f(n) 6 c1 s1 (n). Es sei c > 0 beliebig. Setze c 0 = c c1 . Dann gilt c 0 > 0, und es folgt ∃n0 ∈ N ∀n > n0 : c1 s1 (n) 6 c1 c 0 s2 (n) = cs2 (n). Mit f ∈ O(s1 ) und n2 := max(n0 , n1 ) erhalten wir schließlich ∀n > n2 : f(n) 6 c1 s1 (n) 6 cs2 (n) und damit f ∈ o(s2 ). Anschaulich: Wenn die Funktion s1 (asymptotisch) langsamer als s2 wächst, dann wächst jede Funktion f : N → N, die höchstens so schnell wächst wie s1 , langsamer als s2 . Elementare Lösung Mit der Iterationsmethode und vollständiger Induktion erhalten wir k−1 n X 7 i 2 T (n) = 7T +n = 7 T k +n 2 2 4 n 2 k i=0 für alle k 6 log2 n und S(n) = aS n 4 + n2 = ak S k−1 n X a i 2 + n 4k 16 i=0 für alle k 6 log4 n. Die Iteration bricht jeweils bei T (1) ab, d.h. wenn 2nk = 1 bzw. 4nk = 1 gilt. Das gilt aber genau dann, wenn k = log2 n bzw. k = log4 n. Unter Verwendung der endlichen geometrischen Reihe lassen sich T (n) und S(n) (für a 6= 16) in eine geschlossene Form umformen: 7 4 T (n) = nlog2 7 − n2 = Θ(nlog2 7 ) 3 3 a 1 log4 a 2 S(n) = n + a n a − 16 1 − 16 Um die asymptotische Laufzeit des Algorithmus B zu bestimmen, unterscheiden wir drei Fälle: 7 a<16: Wir erhalten S(n) = c1 nlog4 a + c2 n2 mit c1 = a a−16 6 0 und c2 = 1 a 1− 16 > 0. Daraus folgt S(n) = Θ(n2 ). Wegen limn→∞ n2 nlog2 7 = limn→∞ 1 7 nlog2 4 = 0 gilt n2 ∈ o(nlog2 7 ). Mit Lemma 1 folgt Θ(n2 ) ⊆ O(n2 ) o(nlog2 7 ), ⊆ (Lemma 1) und wir erhalten S(n) = o(nlog2 7 ), d.h. S(n) wächst asymptotisch langsamer als T (n) = Θ(nlog2 7 ).4 Mit anderen Worten: Der Algorithmus B ist asymptotisch schneller als der Algorithmus A. Wir erhalten durch Einsetzen von a = 16 und k = log4 n in die geschlossene Form von S(n): a=16: S(n) = 16log4 n + n2 log4 n = nlog4 16 + n2 log4 n = n2 + n2 log4 n = Θ(n2 log n) Wegen limn→∞ n2 log2 n nlog2 7 = 0 gilt n2 log2 n ∈ o(nlog2 7 ). Mit Lemma 1 folgt Θ(n2 log n) ⊆ O(n2 log n) ⊆ o(nlog2 7 ), (Lemma 1) und wir erhalten S(n) = o(nlog2 7 ), d.h. S(n) wächst asymptotisch langsamer als T (n) = Θ(nlog2 7 ). Mit anderen Worten: Der Algorithmus B ist asymptotisch schneller als der Algorithmus A. a>16: Wir erhalten S(n) = c1 nlog4 a + c2 n2 mit c1 = a a−16 > 1 und c2 = 1 a 1− 16 6 0. Daraus folgt S(n) = Θ(nlog4 a ). Es soll nun der größte Wert von a ∈ N bestimmt werden, für den der Algorithmus B asymptotisch schneller als der Algorithmus A ist. Mit den beiden bereits behandelten 4 Hier fließt ein, dass stets o(f) ∩ Ω(f) = ∅ gilt. 8 Fällen wissen wir, dass a > 16 gelten muss. Weiterhin gilt nlog4 a = o(nlog2 7 ) nlog4 a =0 n→∞ nlog2 7 ⇔ lim nlog4 a−log2 7 = 0 ⇔ lim n→∞ ⇔ log4 a − log2 7 < 0 1 ⇔ log2 a < log2 7 2 ⇔ log2 a < 2 log2 7 ⇔ log2 a < log2 72 = log2 49 ⇔a < 49 Wir erhalten also, zusammen mit Lemma 1, dass O(nlog4 a ) ⊆ o(nlog2 7 ) genau dann gilt, wenn a < 49. Somit ist der größte ganzzahlige Wert von a, für den S(n) asymptotisch langsamer wächst als T (n) = Θ(nlog2 7 ), gerade a = 48. Mit anderen Worten: Der größte ganzzahlige Wert von a, für den der Algorithmus B asymptotisch schneller ist als der Algorithmus A, ist a = 48. Die drei untersuchten Fälle a < 16, a = 16 und a > 16 entsprechen gerade genau den drei Fällen des Master-Theorems (vgl. [2], S. 73), das wir nun statt der Iterationsmethode anwenden, um die Rekurrenzgleichungen T (n) und S(n) zu lösen. Offensichtlich gilt n2 = Ω(nlog2 (7)− ) mit := log2 74 > 0. Mit dem Master-Theorem erhalten wir sofort T (n) = Θ(nlog2 7 ). Verwendung des Master Theorems n 2 2 Es gilt n2 = Ω(nlog4 (a)+ ) mit := log4 16 a > 0. Des Weiteren gilt a( 4 ) 6 cn für fast alle n ∈ N und eine Konstante 0 < c < 1. Nach dem Master-Theorem gilt nun a<16: S(n) = Θ(n2 ). Genau wie bei der elementaren Lösung begründet man nun S(n) = o(nlog2 7 ). a=16: Es gilt offensichtlich n2 = Θ(nlog4 a ). Mit dem Master-Theorem folgt S(n) = Θ(n2 log n). Genau wie bei der elementaren Lösung zeigt man nun S(n) = o(nlog2 7 ). a>16: a 16 > log4 a Es gilt n2 = Ω(nlog4 (a)− ) mit := log4 S(n) = Θ(n 0. Mit dem Master-Theorem folgt ). Wie bei der elementaren Lösung beweist man nun, dass der größte ganzzahlige Wert von a, für den der Algorithmus B asymptotisch schneller ist als der Algorithmus A, gerade a = 48 ist. 9 Aufgabe 8 Wir verfolgen den folgenden rekursiven Ansatz, um n Scheiben von einem Startstapel auf einen Zielstapel unter Zuhilfenahme eines Hilfsstapels zu bewegen: 1. Bewege die n − 1 kleinsten Scheiben vom Startstapel auf den Hilfsstapel. 2. Bewege die größte der n Scheiben vom Startstapel auf den Zielstapel. 3. Bewege die n − 1 kleinsten Scheiben vom Hilfsstapel auf den Zielstapel. Wir gehen zunächst davon aus, dass dieser Ansatz immer eine korrekte Lösung liefert und analysieren die Komplexität. Es sei T (n) die minimale Anzahl Bewegungen, die notwendig sind, um n Scheiben von einem Stapel auf einen anderen Stapel (unter den vorgegebenen Regeln) zu transportieren. Dann benötigt Schritt 1 T (n − 1) Bewegungen, Schritt 2 eine Bewegung und Schritt 3 wiederum T (n − 1) Bewegungen. Folglich können n Scheiben in (höchstens) 2T (n − 1) + 1 Schritten bewegt werden, d.h. es gilt T (n) 6 2T (n − 1) + 1 für n > 0. Damit sind 2T (n − 1) + 1 Bewegungen hinreichend zur Lösung; wir müssen noch argumentieren, dass diese Anzahl Bewegungen auch notwendig ist [4]: Irgendwann muss die größte der n Scheiben auf den Zielstapel bewegt werden. Zu dem Zeitpunkt müssen die n − 1 kleinsten Scheiben bereits auf einen einzigen anderen Stapel bewegt worden sein. Dies benötigt mindestens T (n − 1) Bewegungen. Wir könnten die größte Scheibe mehr als einmal bewegen, aber nachdem wir die größte Scheibe das letzte mal bewegt haben, müssen wir die n − 1 kleinsten Scheiben von ihrem Stapel auf die größte Scheibe transportieren. Dies benötigt wiederum mindestens T (n − 1) Bewegungen. Also gilt T (n) > 2T (n − 1) + 1, und wir erhalten insgesamt T (n) = 2T (n − 1) + 1, falls n > 0 0, falls n = 0. Mit vollständiger Induktion zeigt man leicht, dass T (n) = 2n − 1 für alle n > 0 gilt.5 Wir geben mit diesen Vorüberlegungen nun einen Algorithmus an, der das Problem „Türme von Hanoi“ korrekt löst: Hanoi(n) 1 Hanoi-Recursive(n, A, B, C) 5 Interessanterweise ist 2n − 1 gerade die n.te Mersenne’sche Zahl. 10 Hanoi-Recursive(i, start, hilf , ziel) 1 2 3 4 if i > 0 then Hanoi-Recursive(i − 1, start, ziel, hilf ) Bewege die oberste Scheibe von start nach ziel. Hanoi-Recursive(i − 1, hilf , start, ziel) Mit dem Vorstehenden ist die Laufzeit des Algorithmus Hanoi Θ(2n ). Zur Korrektheit: Wir zeigen per vollständiger Induktion nach n ∈ N0 , dass der Aufruf Hanoi-Recursive(n, a, b, c) eine korrekte Bewegungsfolge liefert, wann immer n Scheiben auf einem beliebigen Startstapel a liegen und zu einem beliebigen Zielstapel c 6= a unter Zuhilfenahme eines Hilfsstapels b (b 6= a und b 6= c) transportiert werden sollen, wobei auf b und c, sofern sie nicht leer sind, nur Scheiben liegen, die größer sind als die größte der zu transportierenden n Scheiben. Für n = 0 ist die Aussage trivial und damit der Induktionsanfang erfüllt. Im Induktionsschritt schließen wir von n − 1 auf n > 0. Nach Induktionsvoraussetzung bewegt der Aufruf Hanoi-Recursive(n − 1, a, c, b) die oberen n − 1 Scheiben korrekt von a nach b, denn die Rollen von b und c als Hilfs- bzw. Zielstapel können vertauscht werden. Die größte der n zu transportierenden Scheiben wird daraufhin auf den Stapel c transportiert, was zulässig ist, da c nach Voraussetzung entweder leer ist, oder auf c nur Scheiben liegen, die größer sind als die größte der n zu transportierenden Scheiben. Nun liegen auf dem Stapel a, sofern er nicht leer ist, nur Scheiben, die größer sind als die größte der n − 1 nun zu verschiebenden Scheiben. Entsprechendes gilt für den Stapel c. Der anschließende Aufruf Hanoi-Recursive(n − 1, b, a, c) bewegt also nach Induktionsvoraussetzung die n − 1 obersten Scheiben korrekt vom Stapel b auf den Stapel c. Damit sind alle n Scheiben vom Startstapel a korrekt auf den Zielstapel c transportiert worden. Es folgt unmittelbar die Korrektheit des Algorithmus Hanoi. Die Türme von Hanoi sind ein Beispiel für ein praktisch unlösbares Problem: Die Anzahl Bewegungen, die notwendig und hinreichend ist, um n > 0 Scheiben von einem Stapel auf einen anderen Stapel zu bewegen ist T (n) = 2n − 1; wir haben gesehen, dass sich dieses Ergebnis nicht verbessern lässt. Diese Anzahl ist aber exponentiell in der Anzahl n der Scheiben. Wie in Tabelle 1 ersichtlich, wird die Anzahl der Bewegungen selbst für „kleine“ n „astronomisch“. Die in der Tabelle 1 angegebene Abschätzung für die benötigte Zeit, ergibt sich unter der Annahme, dass ein Mönch eine Scheibe pro Sekunde verschieben kann und bis zur Vollendung der Aufgabe ohne Unterbrechung arbeitet. Zieht man in Betracht, dass unser Universum etwa 13,5 Milliarden Jahre alt ist, so besteht also zunächst kein Anlass zu Weltuntergangsstimmung. 11 Anzahl Scheiben 5 10 20 30 40 60 64 Minimale Anzahl Bewegungen 31 1.023 1.048.575 1.073.741.823 1.099.511.627.775 1.152.921.504.606.846.975 18.446.744.073.709.551.615 Benötigte Zeit 31 Sekunden 17 Minuten 12 Tage 34 Jahre 348 Jahrhunderte 36 Milliarden Jahre 584 Milliarden Jahre Tabelle 1: Praktische Unlösbarkeit der Türme von Hanoi [7] Aufgabe 9 Definitionen und Problemstellung Definition 2. Es seien p1 , p2 ∈ Rd Punkte. Wir sagen, dass der Punkt p1 = (x1 , . . . , xd ) den Punkt p2 = (x10 , . . . , xd0 ) dominiert, falls xk > xk0 für alle k ∈ { 1, 2, . . . , d }. Im Falle des R2 heißt das für p1 = (x1 , y1 ) und p2 = (x2 , y2 ), dass x1 > x2 und y1 > y2 gilt. Es sei P ⊆ Rd eine Menge von Punkten. Ein Punkt m ∈ P heißt maximal (in P), falls er von keinem anderen Punkt aus P dominiert wird. Wir nennen die Menge M ⊆ P der maximalen Punkten in P die Pareto-Menge von P. Abbildung 1 verdeutlicht die Begriffe Dominanz und Maximalität. Der Punkt p2 wird genau dann von einem Punkt p1 dominiert, falls sich dieser „rechts oberhalb“ (durch Schraffierung hervorgehoben) von p2 befindet. In der Abbildung wird p2 also von p1 dominiert. Würde sich kein Punkt in der schraffierten Ebene befinden, so wäre p2 maximal. In diesem Beispiel ist p1 ein maximaler Punkt, p2 hingegen nicht. y p1=(x1,y1) p2=(x2,y2) x Abbildung 1: Dominanz und Maximalität im R2 Gegeben sei nun eine endliche Menge P ⊆ R2 . Wir wollen die Pareto-Menge von P berechnen. Dieses Problem ist auch bekannt als Maximum Vector Problem. 12 Der folgende Algorithmus ist ein Spezialfall des in [5, 6] vorgestellten Algorithmus, der in Zeit O(n(log n)d−2 ) + O(n log n) die maximalen Punkte einer vorgegebenen endlichen Menge P ⊆ Rd berechnet. Im Falle des R2 ist die worst-case-Laufzeit O(n log n) optimal. Beim Beweis der Optimalität argumentiert man über untere Schranken für bestimmte Sortierverfahren. Algorithmus Maximal-Points(P) Eingabe: Array P von Punkten mit x-Koordinate P[k][1] und y-Koordinate P[k][2] Ausgabe: Die Pareto-Menge von P als Array 1 Sortiere P primär nach x-Koordinate und sekundär nach y-Koordinate, sodass P[1][1] 6 P[2][1] 6 . . . 6 P[length[P]][1] und P[j][2] 6 P[j + 1][2] 6 P[j + m][2], falls P[i][1] = P[i + 1][1] für i ∈ { j, j + 1, . . . , j + m − 1 }. 2 return Maximal-Points-Recursive(P) Maximal-Points-Recursive(P) 1 2 3 4 5 6 7 8 if length[P] = 1 then return P else Pl ← P[1 . . b n 2 c] n Pr ← P[b 2 c + 1 . . length[P]] Ml ← Maximal-Points-Recursive(Pl ) Mr ← Maximal-Points-Recursive(Pr ) M ← Merge-Maximal-Points(Ml , Mr ) return M Merge-Maximal-Points(Ml , Mr ) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 nl ← length[Ml ] nr ← length[Mr ] ymax ← Mr [1][2] for i ← 2 to nr do if Mr [i][2] > ymax then ymax ← Mr [i][2] M ← newArray(0) for i ← 1 to nl do if Ml [i][2] > ymax then length[M] ← length[M] + 1 M[length[M]] ← Ml [i] length[M] ← length[M] + nr M[length[M] − nr + 1 . . length[M]] ← Mr return M Der Algorithmus Maximal-Points wird auf einer durch ein Array kodierten Punktmenge P ⊆ R2 aufgerufen (vgl. Abbildung 2). Diese wird primär nach der x-Koordinate und sekundär nach der y-Koordinate aufsteigend sortiert. Der Erläuterung und Korrektheit 13 Sortiervorgang in Zeile 1 des Algorithmus Maximal-Points kann z.B. mit einem stabilen Sortieralgorithmus wie Merge-Sort durchgeführt werden, indem durch zwei hintereinander ausgeführte Aufrufe von Merge-Sort die Elemente des Arrays P zunächst nach der y-Koordinate und dann nach der x-Koordinate sortiert werden. Abbildung 2: Eine Punktmenge P ⊆ R2 Nach dem Sortieren wird im Algorithmus Maximal-Points-Recursive rekursiv auf der Menge P operiert. Die Menge der Punkte P wird zunächst in eine „linke“ Teilmenge Pl und eine „rechte“ Teilmenge Pr annähernd gleicher Größe geteilt (vgl. Abbildung 3). Pl Pr Abbildung 3: Aufteilung der Punktmenge P in zwei Teilmengen Pl und Pr Für die beiden Mengen Pl und Pr werden rekursiv jeweils die Mengen Ml und Mr der maximalen Punkte berechnet. Die maximalen Punkte von Pl bzw. Pr sind in Abbildung 4 durch Linien miteinander verbunden. Pl Pr Abbildung 4: Rekursive Berechnung der maximalen Punktmengen Ml und Mr Anschließend werden die beiden Paretomengen Ml und Mr mit dem Algorithmus Merge-Maximal-Points zusammengefügt, und das Ergebnis M wird zurückgegeben. Dies ist offensichtlich ein Divide-&-Conquer-Ansatz. Der Rekursionsabbruch findet bei einer Menge, die aus genau einem Punkt besteht, statt. Die Menge der maximalen Punkte besteht dann trivialerweise genau aus diesem Punkt. Die meiste Arbeit steckt im Zusammenfügen der beiden maximalen Mengen Ml und Mr zu einer Menge M, die maximal für P sein soll: 14 Zunächst beobachtet man, dass kein Punkt pr = (xr , yr ) ∈ Pr von einem Punkt pl = (xl , yl ) ∈ Pl dominiert werden kann: Da das Array P primär nach x-Koordinate und sekundär nach y-Koordinate (aufsteigend) sortiert ist, gilt xl 6 xr , und im Falle xl = xr gilt yl 6 yr . Folglich wird pr nicht von pl dominiert.6 Es kann aber sehr wohl Punkte in Pr geben, die Punkte aus Pl dominieren. Dabei genügt es, die Punkte in Ml zu bestimmen, die von Punkten aus Mr dominiert werden. Es sei ml = (xl , yl ) ∈ Ml beliebig. Für alle Punkte mr = (xr , yr ) ∈ Mr gilt dann xr > xl . Der Punkt ml ist also in P genau dann maximal, wenn es keinen Punkt mr = (xr , yr ) ∈ Mr mit yr > yl gibt. Es genügt daher den Wert einer größten y-Koordinate ymax bzgl. aller Punkte aus Mr zu berechnen und dann zu bestimmen, welche Punkte aus Ml eine größere y-Koordinate als ymax besitzen. Genau diese Punkte aus Ml sind maximal in P. Es gibt auch eine grafische Interpretation: Man zieht eine zur x-Achse parallele Gerade durch die Punkte in Mr mit der größten y-Koordinate (vgl. Abbildung 5). Pl Pr Abbildung 5: Berechnung von ymax Alle Punkte in Ml , die sich auf oder unterhalb dieser Geraden befinden, sind nicht maximal in P. Die Punkte in Ml oberhalb dieser Geraden sind hingegen maximal in P. Die for-Schleife in Zeile 8 des Algorithmus Merge-Maximal-Points übernimmt gerade die Punkte aus Ml , die maximal in P sind, in das Array M (vgl. Abbildung 6). Pl Pr Abbildung 6: Elimination von Punkten in Ml , die von Punkten in Mr dominiert werden Wir hatten zuvor beobachtet, dass kein Punkt aus Pr von einem Punkt aus Pl dominiert werden kann, d.h. alle Punkte in Pr sind maximal in P. Daher werden diese in Zeile 13 des Algorithmus Merge-Maximal-Points am Ende des Arrays M angefügt. Insgesamt erhalten wir die Pareto-Menge M von P (vgl. Abbildung 7). Man bemerke außerdem, dass die Menge M wiederum primär nach x-Koordinate und sekundär nach y-Koordinate (aufsteigend) sortiert ist. 6 Für die Argumentation haben wir an dieser Stelle implizit vorausgesetzt, dass die Punkte im Array P paarweise verschieden sind. 15 Abbildung 7: Die Pareto-Menge M von P Es bezeichne T (n) die worst-case-Laufzeit des Algorithmus MaximalPoints-Recursive. Dann gilt Laufzeitanalyse T (n) = 2T n + cn = O(n log n) 2 Die worst-case-Laufzeit lässt sich noch verbessern, indem zur Verwaltung der maximalen Punkte ein balancierter binärer Suchbaum verwendet wird. Man erhält dann die Rekurrenzgleichung T (n) = 2T ( n 2 ) + c log n = O(n). Die worst-case-Laufzeit des Algorithmus Maximal-Points ist aber in beiden Fällen O(n log n) aufgrund des vorab durchgeführten Sortiervorgangs. Das Maximum Vector Problem tritt in vielen unterschiedlichen Kontexten auf und ist als solches daher tiefgehend untersucht worden. Es hat neue Bedeutung durch sogenannte Skyline Queries (erstmals eingeführt in [1]) für relationale Datenbanken gewonnen. Bei Skyline Queries werden die maximalen Tupel über einer Menge von Tupeln gesucht. Bestimmte Spalten, deren zugrundeliegenden Domänen linear geordnet sind, werden als Skyline-Kriterien designiert. Dominanz wird dann bzgl. dieser Kriterien definiert. Die nicht-dominierten, d.h. maximalen Tupel formen dann die Skyline-Menge und somit das Ergebnis einer Skyline Query. Beispiel: Anwendungen Name Aga Fol Kaz Neo Tor Uma Sterne ** * * *** *** ** Distanz 0.7 1.2 0.2 0.2 0.5 0.5 Preis 1175 1237 750 2250 2550 980 Tabelle 2: Die Tabelle Hotel [3] Die folgende Skyline-Abfrage auf einer Tabelle Hotel, die die Spalten Name, Adresse, Sterne (Qualitätsmaß), Distanz (Distanz zum Strand) und Preis besitzt, fragt nach den Hotels, für die sich kein Hotel finden lässt, das zugleich mindestens genauso viele Sterne besitzt, mindestens genauso nah am Strand ist und mindestens genauso günstig ist: 16 SELECT Name, Adresse FROM Hotel SKYLINE OF Sterne MAX, Distanz MIN, Preis MIN; Das Resultat der Abfrage ist in der Tabelle 2 fett hervorgehoben. Man kann die Daten aus Tabelle 2 als Punkte im dreidimensionalen Raum visualisieren. Die Aufgabe besteht dann darin die maximalen Punkte zu bestimmen. Das entspricht gerade der Ergebnismenge der obigen Skyline-Abfrage. Aufgabe 10 Es sei G = (V, E) ein ungerichteter Graph mit V = { v1 , . . . , vn }. Gegeben sei weiterhin die Adjazenzmatrix A = (aik ) ∈ { 0, 1 }n×n von G, sodass gilt aik = 1 ⇔ { vi , vk } ∈ E (r) Es sei r > 1 beliebig. Setze A(r) := Ar und sei A(r) = (aik ). Wir behaupten, dass dann (r) aik die Anzahl der Wege der Länge (genau) r vom Knoten vi zum Knoten vk in G angibt. Dazu führen wir Induktion nach r. Der Induktionsanfang für r = 1 ist klar. Es sei nun r > 1 und die Behauptung korrekt für r − 1. Seien i, k ∈ { 1, 2 . . . , n }. Wir wollen die Anzahl der Wege der Länge r vom Knoten vi zum Knoten vk in G ermitteln. Dazu überlegt man sich, dass sich ein Weg der Länge r aus einem Weg der Länge r − 1 und einer anschließenden Kante zusammensetzt. Es genügt also alle Kanten vj , vk ∈ E, die zum Knoten vk inzident sind, zu betrachten und die Anzahl der Wege von vi nach vj der Länge r − 1 jeweils aufzusummieren, d.h. die Anzahl der Wege von vi nach vk der Länge r ist gegeben durch X { vj ,vk }∈E (r−1) aij = n X (r−1) aij ajk = (A(r−1) A)ik = (A(r) )ik . j=1 Damit ist auch der Induktionsschritt gezeigt und die Behauptung folgt mit dem Prinzip der vollständigen Induktion. Berücksichtigt man nun, dass Dreiecke in G Kreise der Länge 3, also Wege der Länge 3 mit gleichem Start- und Endknoten sind, so genügt es im Wesentlichen die Spur der Matrix D := A3 , d.h. die Summe der Diagonaleinträge von D zu berechnen. Da es innerhalb eines Dreiecks genau 6 Kreise der Länge 3 gibt, muss der so berechnete Wert anschließend noch durch 6 dividiert werden, um die Anzahl der Dreiecke in G zu bestimmen. Der nachfolgende Algorithmus Count-Triangles führt die beschriebene Idee aus. 17 Count-Triangles(A) 1 2 3 4 5 6 7 Eingabe: n × n-Adjazenzmatrix kodiert als zweidimensionales Array A D ← newArray(rows[A], rows[A]) D ← Strassen-Matrix-Multiplication(A, A) D ← Strassen-Matrix-Multiplication(D, A) s←0 for i ← 1 to rows[D] do s ← s + D[i][i] return 6s Die Korrektheit des Algorithmus folgt mit den vorhergehenden Überlegungen. Die Laufzeit ist Θ(nlog2 7 ) wenn der Algorithmus von Strassen zur Matrixmultiplikation verwendet wird. Literatur [1] Börzsönyi, S. ; Kossmann, D. ; Stocker, K.: The Skyline Operator. In: 17th International Conference on Data Engineering (ICDE’ 01). Washington - Brussels - Tokyo : IEEE, April 2001, S. 421–432 [2] Cormen, Thomas H. ; Leiserson, Charles E. ; Rivest, Ronald L. ; Stein, Clifford: Introduction to Algorithms. Second Edition. MIT Press and McGraw-Hill Book Company, 2001 [3] Godfrey, Parke ; Shipley, Ryan ; Gryz, Jarek: Algorithms and analyses for maximal vector computation. In: VLDB J 16 (2007), Nr. 1, S. 5–28 [4] Graham, Ronald L. ; Knuth, Donald E. ; Patashnik, Oren: Concrete Mathematics. Second Edition. Reading, Massachusetts : Addison-Wesley, 1994 [5] Kung, H. T. ; Luccio, Fabrizio ; Preparata, Franco P.: On Finding the Maxima of a Set of Vectors. In: Journal of the ACM 22 (1975), Nr. 4, S. 469–476 [6] Preparata, F. P. ; Shamos, M. I.: Computational Geometry: an Introduction. Berlin : Springer, 1985 [7] Wikipedia. Türme von Hanoi. BCrme_von_Hanoi. 5 2007 URL:http://de.wikipedia.org/wiki/T%C3% 18