http://www.mpi-sb.mpg.de/~hannah/info5-ws00 R S IS UN WS 2000/2001 E R SIT S Bast, Schömer SA IV A Grundlagen zu Datenstrukturen und Algorithmen A VIE N Lösungsvorschläge für das 3. Übungsblatt Letzte Änderung am 14. November 2000 Aufgabe 1 (a) Bei einer Addition zweier n × n-Matrizen werden n2 primitive Additionen benötigt. Für eine Multiplikation zweier n × n-Matrizen werden n2 · n = n3 primitive Multiplikationen und n2 · (n − 1) primitive Additionen, also 2n3 − n2 primitive Operationen benötigt. (b) Behauptung: Für n = 2k , k ∈ , berechnet der Algorithmus das Produkt C zweier n × nMatrizen A und B korrekt. Also cij = (A · B)ij für alle i, j = 1, ..., n Zunächst kann man durch Ausmultiplizieren feststellen, daß die Formeln auf dem Übungsblatt stimmen. Beispiel: M1 + M 2 − M 4 + M 6 = (A12 − A22 )(B21 + B22 ) + (A11 + A22 )(B11 + B22 ) − (A11 + A12 )B22 + A22 (B21 − B11 ) = A12 B21 + A12 B22 − A22 B21 − A22 B22 + A11 B11 + A11 B22 + A22 B11 + A22 B22 −A11 B22 − A12 B22 + A22 B21 − A22 B11 = A11 B11 + A12 B21 . Weiter betrachten wir zwei Fälle. 1. Fall: n = 2: Die Matrizen A und B bestehen aus vier ganzen Zahlen. In diesem Fall berechnet der Algorithmus offensichtlich das korrekte Ergebnis. 2. Fall: n > 2: Für i,j = 1, ..., n/2 ist nach dem Strassen-Algorithmus (C11 )ij = (A11 · B11 + A12 · B21 )ij = (A11 · B11 )ij + (A12 · B21 )ij . Nach Konstruktion sind A11 , B11 , A12 und B21 Matrizen der Größe n/2 × n/2 mit (A11 · B11 )ij = Also (C11 )ij = n/2 X aik bkj und k=1 n/2 X k=1 aik bkj + n X k=n/2+1 (A12 · B21 )ij = aik bkj = n X k=1 n X aik bkj . k=n/2+1 aik bkj = (A · B)ij für i,j = 1, ..., n/2. Analog zeigt man für C12 , C21 und C22 , dass sie die korrekten Teilmatrizen aus A · B liefern. Analyse: Bei der Multiplikation zweier n×n-Matrizen mit dem Strassen-Algorithmus werden 7 Multiplikationen und 18 Additionen zweier n/2 × n/2-Matrizen benötigt. (c) Sei G(n) die Anzahl der primitiven Operationen bei der Multiplikation zweier n × nMatrizen. Dann gilt: 1 falls n = 1 G(n) = 7G(n/2) + 18(n/2)2 falls n ≥ 2 Aus dem Master Theorem für Rekursionsgleichungen folgt (a = 7, b = 2, c = 9/2, d = 2, logb a = log 7 > 2 = d): G(n) = O(nlog 7 ) Insbesondere ist log 7 < 3, so daß der Strassen-Algorithmus besser ist als der naive Algorithmus. Aufgabe 2 a) Algorithmus: bool is_prime(integer m) { if (m < 2) return false; integer i = 2; while (i * i <= m) { if (m % i == 0) return false; } return true; } b) Die while-Schleife läuft höchstens √ // 0,1 keine Primzahlen // solange i <= Wurzel von m // // falls i Teiler von m // keine Primzahl // kein Teiler gefunden m Mal durch; da m < B n also höchstens B n/2 mal. Ein Durchlauf kostet O(n2 ) primitive Operationen für die Multiplikation zum Testen der Abbruchbedingung plus O(n2 ) primitive Operationen für die Berechnung des Restes bei der Division von m durch i (laut Hinweis von der Mailingliste; vgl. Aufgabe 5 vom 1. Übungsblatt). Zusammen ergibt sich also O(B n/2 · n2 ); um O(B n ) zu zeigen, genügt es also, sich O(n2 ) = O(B n/2 ) klar zu machen! Aufgabe 3 a) Schreibe logB (k) = ln(k) . ln(B) k· Dann ist zu zeigen: ln(B) ln(k) + ≤k−2 B ln(B) für k ≥ 3, B ≥ 14. Wir zeigen k· ln(B) B + ln(k) ln(B) ln(k) k ≤ 2 ln(B) + ln(B) 1 k = ln(B) + ln(k) 2 k 1 + ln(k) ≤ ln(14) 2 ≤ k−2 (siehe Teil b)) (siehe Teil c)). b) Es ist Wir definieren die Funktion ⇔ ⇔ ln(B) B 2 1 B 2 (ln(B)) − (ln(B))2 1 ≤ 2 ln(B) ≤ 12 B ≥ 0. 1 f (x) := x − (ln(x))2 2 und zeigen, daß f (x) ≥ 0 ist für x ≥ 14. (i) Es ist 1 2 ln(x) − 2 x 2(ln(x) − 1) f 00 (x) = . x2 f 0 (x) = (ii) Für x ≥ 14 ist ln(x) ≥ ln(14) > 1. Es folgt f 00 (x) ≥ 2(ln(14) − 1) >0 x2 für x ≥ 14. Also ist f 0 (x) für x ≥ 14 monoton wachsend. (iii) Es ist f 0 (14) = 1 2 ln(14) − > 0. 2 14 Es folgt, daß f 0 (x) > 0 für x ≥ 14. Also ist die Funktion f für x ≥ 14 monoton wachsend. (iv) Es ist f (14) = 7 − (ln(14))2 = 0.035376 > 0. Es folgt f (x) > 0 für x ≥ 14. c) Zu zeigen ist k 1 + ln(k) ≤ k − 2 ln(14) 2 für k ≥ 3. Diese Ungleichung ist äquivalent zu 1 ln(k) k 1− −2− ≥ 0. 2 ln(14) ln(14) Es sei 1 g(x) = x 1 − 2 ln(14) −2− ln(x) . ln(14) Wir zeigen jetzt, daß g(x) > 0 ist für x ≥ 3. Es ist g 0 (x) = 1 − Für x ≥ 3 folgt 1 1 − 2 ln(14) x ln(14) 1 1 − ≥ 0.684 > 0. 2 ln(14) 3 ln(14) Also ist für x ≥ 3 die Funktion g(x) monoton wachsend. Es ist ln(3) 1 ≥ 0.015 > 0. −2− g(3) = 3 1 − 2 ln(14) ln(14) g 0 (x) ≥ 1 − Aufgabe 4 Das ε in der Laufzeitabschätzung O(n1+ε ) ist eine Konstante. Diese ist zwar prinzipiell frei wählbar (vorausgesetzt, sie ist echt positiv), allerdings hängt davon ab, in wieviele Teilstücke ak−1 , . . . , a0 , bk−1 , . . . , b0 die Eingabe a, b aufgeteilt wird. Wenn jetzt ε = log1 n gesetzt wird, erhalten wir für 2 jedes n “einen anderen” Algorithmus, da nun auch k nicht mehr konstant ist. Insbesondere werden sich die Konstanten, die in der O-Notation versteckt sind, ebenfalls ändern. Etwas formaler läuft die Argumentation so: Für ε → 0 gilt notwendigerweise k → ∞. Im Skript wird abgeschätzt: 1 k = 2x ⇒ T (n) = O(n1+ x ). Für 1 x =ε= 1 log2 n gilt dann also k = n für alle n. D.h., jede Eingabe der Länge n wird in n einzelne Bits zerlegt. Die Rekursionsgleichung vereinfacht sich dadurch zu T (n) ≤ (2n − 1)T (1) + cn. Der entscheidende Punkt ist nun aber, dass c gar keine Konstante mehr ist, sondern (linear) von n abhängt: Die Rekonstruktion der n Koeffizienten des Polynoms C geschieht jetzt durch Multiplikation mit der Inversen der (2n − 1) × (2n − 1)Vandermonde Matrix. Statt cn werden nun also Θ(n2 ) Multiplikationen (von höchstens n-stelligen Zahlen) durchgeführt, also gilt auch T (n) = Ω(n2 ). Noch etwas genauer: Der verallgemeinerte Algorithmus zerlegt das Problem, zwei n-stellige Zahlen zu multiplizieren, in 2k − 1 Teilprobleme der Größe k. Das Zerlegen kostet dabei wie schon bei clever_mult keine primitiven Operationen (man muss ja nur“ shiften). Primitive Operationen ” werden aber beim Zusammensetzen der Teillösungen benötigt, und zwar bei der Berechnung von c0 C(0) c1 C(1) −1 = V · .. . .. . . c2k−2 C(2k − 2) Wieviele primitive Operationen dabei genau benötigt werden ist gar nicht so einfach zu bestimmen; was man aber sofort sieht ist, dass die C(0), . . . , C(2k − 2) alle höchstens n Stellen (sogar höchstens 2 · n/k) haben, und dass die Matrix V −1 Θ(k 2 ) Einträge hat. Daraus läßt sich nun einerseits folgern, dass sich das Matrixprodukt für konstantes k mit O(n) primitiven Operationen berechnen läßt. So wird in den Course Notes argumentiert, und daraus folgt dann auch die Rekursionsgleichung T (n) ≤ (2k − 1) · T (n/k) + c · n, für irgendeine Konstante c, und somit nach dem Master Theorem T (n) = O(nlogk (2k−1) ). Andererseits braucht man auch bestimmt Ω(k 2 ) Operationen, um das Matrixprodukt zu berechnen. Da man nun, um ε = 1/ log2 n zu erreichen, k = Θ(n) wählen muss (also z.B. k = n, was heißen würde, dass man a und b in jeweils n einstellige Zahlen aufspaltet), gilt für T , dass T (n) ≥ (2k − 1) · T (n/k) + c0 · n2 , für irgendeine Konstante c0 , und somit bestimmt T (n) = Ω(n2 ) (schon die erste Rekursionsstufe kostet dann ja Θ(k 2 ) = Θ(n2 ) primitive Operationen). Also nix O(n). Aufgabe 5 Zuerst überprüfen wir, dass für das D aus dem Hinweis D= l Y i=1 blog pi nc pi ≤ l Y logpi n pi = i=1 l Y n = nl , i=1 und weil nach dem Hinweis (Primzahlsatz) l < n/ log4 n ≤ n/ logB n haben wir D < nn/ logB n = B n , d.h. D hat in der Tat höchstens n Stellen (bez. Basis B). Sei jetzt k ∈ {1, . . . , n} beliebig, und sei pα1 1 · · · pαl l mit αi ∈ 0 die (eindeutige) Primzahlzerlegung von k (man beachte, dass Primfaktoren größer als n nicht vorkommen können, da k ≤ n). Dann muss aber für jedes i ∈ {1, . . . , l} gelten dass αi ≤ logpi n (sonst k > n) und damit auch αi ≤ logpi n , womit klar ist, dass k ein Teiler von D ist. Alle Zahlen aus {1, . . . , n} sind also Teiler von D. Sei jetzt k eine natürliche Zahl mit höchstens 2+dlogB ne Stellen, und damit k ∈ {1, . . . , B 3 n}, und α 3 sei pα1 1 · · · pl0 l0 die Primzahlzerlegung von k, wobei p1 , . . . , p0l jetzt die Primzahlen aus {1, . .. , B n} sind. Dann sieht man leicht, dass k kein Teiler von D ist genau dann wenn αi ≥ logpi n für ein 0 i ∈ {1, . . . , l } (man beachte dass logpi n = 1 für i > l). dlogp ne Für jedes i ∈ {1, . . . , l0 } ist aber pi i ≥ n, so dass in einer Folge von direkt aufeinanderfoldlogp ne genden Zahlen höchstens jede n-te Zahl durch pi i teilbar sein kann. Für jedes i ∈ {1, . . . , l0 } dlogp ne können also höchstens ein n-tel aller Zahlen aus {1, . . . , B 3 n}, also höchstens B 3 , durch pi i teilbar sein. Es folgt, dass es in der Menge {1, . . . , B 3 n} höchstens l0 · B 3 Nichtteiler von D geben kann. Da nach Primzahlsatz l0 < B 3 n/ log4 (B 3 n), sind also höchstens B 3 n · B 3 / log4 (B 3 n) aller Zahlen aus {1, . . . , B 3 n}, also ein Bruchteil von höchstens O(1/ log n), keine Teiler von D. Für unseren Checker heißt das, das man nicht einfach, wie man vielleicht intuitiv annehmen möchte, einen beliebigen zufälligen k-stelligen Modulus wählen darf, für k = 2 + dlogB ne (das n hier ist das 2n aus der Vorlesung). Die Erfolgswahrscheinlichkeit ist wirklich nur dann so hoch, wenn man eine zufällige Primzahl wählt. (Man beachte auch, dass die Wahrscheinlichkeit, dass eine zufällig gewählte k-stellige Zahl eine Primzahl ist, gerade Θ(1/ log n) war.)