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 1. Ü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 (O-Kalkül, 8 Punkte) Aufgabe 1 Tragen Sie jeweils zutreffend O, Ω oder Θ in das Kästchen ein und beweisen Sie die entstandene Aussage. Verwenden Sie wann immer möglich Θ. a) 3n2 + 14n + 159 ∈ log2 n ∈ log n3 , b) log n2 ∈ 2n ∈ n2 , n3 , n2 log n ∈ n3 (n + 1)! ∈ (n! + 2n ) log3 n , Beweisen oder widerlegen Sie die folgenden Implikationen. c) ∀f, g : N → N : f (n) ∈ O (g(n)) ⇒ 2f (n) ∈ O 2g(n) d) ∀k ∈ N, ∀f, g : N → N : f (n) ∈ O (g(n)) ⇒ f (n)k ∈ O g(n)k Musterlösung: a) 3 + 14 3n2 + 14n + 159 n + = lim 2 n→∞ n→∞ n 1 • Es gilt 3n2 + 14n + 159 ∈ Θ n2 , da lim 159 n2 = 3 und 3n2 + 14n + 159 < ∞. n→∞ n2 • Es gilt 2n ∈ Ω n3 . Zu zeigen ist ∃c > 0 ∃n0 > 0 ∀n ≥ n0 : c · n3 ≤ 2n . Wir zeigen die Aussage für c = 1 und n0 = 10 mit vollständiger Induktion: somit 0 < lim – Induktionsanfang: Es gilt 103 = 1000 < 1024 = 210 . – Induktionsvoraussetzung: Für ein n ∈ N gilt n3 ≤ 2n . – Induktionsschritt: Es gilt (n + 1)3 = n3 + 3n2 + 3n + 1 ≤ n3 + 3n2 + 3n2 + 3n2 = IV n>9 n3 + 9n2 < 2 · n3 ≤ 2 · 2n = 2n+1 . • Es gilt n2 log n ∈ O n3 . Zu zeigen ist ∃c > 0 ∃n0 > 0 ∀n ≥ n0 : n2 log n ≤ c · n3 . Nach dem vorigen Aufgabenteil gilt für alle n ≥ 10 die Ungleichung n3 ≤ 2n und damit auch log n3 ≤ log 2n beziehungsweise 3 · log n ≤ log 2 · n. Das zeigt die gewünschte Behauptung mit c = log3 2 und n0 = 10. b) log n2 2 · log n 2 = lim = und somit insbesondere 3 n→∞ log n n→∞ 3 · log n 3 • Es gilt log n2 ∈ Θ log n3 , da lim log n2 < ∞. n→∞ log n3 0 < lim log2 n 1 = lim = 0. n→∞ log3 n n→∞ log n • Es gilt (n + 1)! ∈ Ω (n! + 2n ). Zu zeigen ist ∃c > 0 ∃n0 > 0 ∀n ≥ n0 : c · (n! + 2n ) ≤ (n + 1)!. Wir zeigen die Aussage für c = 1 und n0 = 2 mit vollständiger Induktion: • Es gilt log2 n ∈ O log3 n , da lim – Induktionsanfang: Es gilt 22 + 2! = 6 = 3!. 1 – Induktionsvoraussetzung: Für ein n ∈ N gilt n! + 2n ≤ (n + 1)!. n≥2 IV – Induktionsschritt: Es gilt (n + 1)! + 2n+1 = n · n! + 2 · 2n ≤ n · n! + n · 2n = n · (n! + 2n ) ≤ n · (n + 1)! < (n + 2) · (n + 1)! = (n + 2)!. c) Die Aussage gilt nicht, ein Gegenbeispiel ist gegeben durch f : N → N, n 7→ 2n und g : N → 2n 4n / O(2n ), da n = 2n → ∞ N, n 7→ n. Es gilt 2n ∈ O(n), da lim = 2, aber es gilt 22n = 4n ∈ n→∞ n 2 für n → ∞. d) Die Aussage gilt. Wegen f (n) ∈ O(g(n)) existiert ein c > 0 und ein n0 > 0, sodass für alle n ≥ n0 gilt f (n) ≤ c · g(n). Daraus folgt, dass für alle n ≥ n0 gilt f (n)k ≤ (c · g(n)))k . D.h. für c0 = ck und n00 = n0 folgt für alle n ≥ n00 die gewünschte Ungleichung f (n)k ≤ c0 · g(n)k . Aufgabe 2 (Schleifeninvariante, 4 Punkte) Gegeben sei folgender Algorithmus in Pseudocode. Gehen Sie dabei vereinfachend davon aus, dass alle auftretenden Zahlen jeweils in eine Speicherzelle passen. Function f (n : N) : N a=0 : N i=0 : N while i < n do b=1 : N j=0 : N while j < i do b:= b + b j:= j + 1 a:= a + b i:= i + 1 return a a) Berechnen Sie die Laufzeit des Algorithmus im O-Kalkül. b) Nennen Sie eine Schleifeninvariante für die innere while-Schleife, d.h. eine Invariante, die vor, nach und am Anfang jedes Schleifendurchlaufes gilt. Diese soll b in Abhängigkeit von j beschreiben. c) Nennen und beweisen Sie eine Schleifeninvariante für die äußere while-Schleife. Diese soll a in Abhängigkeit von i beschreiben. Was gibt der Algorithmus zurück? d) Geben Sie einen optimierten Algorithmus an, der auch ausschließlich mit Additionen und Subtraktionen auskommt und die gleiche Funktion berechnet. Geben Sie die verbesserte Laufzeit im O-Kalkül an. Musterlösung: a) Die innerste Schleife wird n−1 P i= i=0 n(n − 1) mal durchlaufen, damit ergibt sich eine Laufzeit in 2 Θ n2 . b) Die Schleifeninvariante ist b = 2j . c) Es gilt stets a = 2i − 1. Der Algorithmus gibt also die n-te Mersenne-Zahl 2n − 1 zurück. Wir beweisen die Schleifeninvariante induktiv. • Vor der Schleife und am Beginn des ersten Schleifendurchlaufes gilt i = 0 und a = 0 = 20 − 1. 2 • Am Anfang des i-ten Schleifendurchlaufes gelte a = 2i − 1. Wir zeigen, dass dann am Anfang des i + 1-ten Schleifendurchlaufes a = 2i+1 − 1 gilt. Dazu betrachten wir zunächst die innere IV Schleife. Nach b) wird insgesamt 2i berechnet. Damit ergibt sich a = a + 2i = 2i − 1 + 2i = 2i+1 − 1. • Da i in jedem Durchgang um einen Zähler erhöht wird und am Anfang i < n für jede natürliche Zahl n gilt, terminiert die Schleife immer. Bei Beendigung der Schleife gilt i = n und a = 2n − 1. d) Der folgende Algorithmus berechnet die gleiche Funktionalität in Laufzeit Θ(n). Function f (n : N) : N a=1 : N for i := 1 to n do a:= a + a a:= a − 1 return a Aufgabe 3 (Karatsuba-Ofman-Multiplikation, 4 Punkte) Multiplizieren Sie 1701 mit 8472 (das sind Dezimalzahlen!) nach dem Algorithmus von Karatsuba und Ofman. Hinweis: Führen Sie nur Additionen sowie einstellige Multiplikationen direkt aus. Mehrstellige Multiplikationen müssen rekursiv mit Karatsuba-Ofman berechnet werden. Musterlösung: Der Algorithmus: 1: Function recMult(a, b) 2: assert a und b haben n = 2k Ziffern 3: if n = 1 then return a · b 4: Schreibe a als a1 · B k + a0 5: Schreibe b als b1 · B k + b0 6: c11 := recMult(a1 , b1 ) 7: c00 := recMult(a0 , b0 ) 8: c0110 := recMult((a1 + a0 ), (b1 + b0 )) 9: e:= c11 · B 2k + (c0110 − c11 − c00 )B k + c00 10: return e Schema zum Aufschrieb eines Multiplikationsaufrufs: a·b a1 · b1 = recMult(a1 , b1 ) a0 · b0 = recMult(a0 , b0 ) e (a1 + a0 )·(b1 + b0 )=recMult((a1 + a0 ), (b1 + b0 )) Berechnung: Siehe Rekursionsbaum in Abbildung 1. Aufgabe 4 (Algorithmenentwurf, 3 Punkte) Entwerfen Sie einen Algorithmus für das folgende Problem: Gegeben sei eine Menge M von reellen Zahlen. Eine reelle Zahl r ∈ M heißt zusammengesetzt, wenn sich r als Summe r = s + t zweier Elemente s, t ∈ M schreiben lässt. Entwerfen Sie einen Algorithmus in Pseudocode, der für alle r ∈ M entscheidet, ob diese zusammengesetzt sind. Sie dürfen annehmen, dass M als aufsteigend sortiertes Array vorliegt. Ihr Algorithmus muss asymptotisch Laufzeit O n2 haben. Begründen Sie, warum der Algorithmus diese Laufzeit erreicht. 3 1701·8472 17· 84 =1428 01· 72 = 72 18·156=2808 14410872 17·84 1428 s HHH HH HH HH HH HH s s Hs 1· 8 = 8 7· 4 =28 8·12=96 01·72 0·7=0 1·2=2 1·9=9 72 s 96 0·1= 0 8·2=16 8·3=24 1·15 15 2808 1·15= 15 8· 6 = 48 9·21=189 s " " " " " " " " " " " " " s" s s 8·12 18·156 0·1=0 1·5=5 1·6=6 9·21 189 0·2= 0 9·1= 9 9·3=27 Abbildung 1: Berechnungsbaum Musterlösung: Der Algorithmus führt für jedes r ∈ M folgendes durch: Man verwendet zwei Indizes in M, nämlich l = 0 und u = |M | − 1, die anfangs auf das größte und das kleinste Element zeigen. Nun wird in einer Schleife geprüft: Wenn M [l] + M [u] = r, dann wird für r ja ausgegeben und die Schleife abgebrochen. Wenn M [l] + M [u] > r, dann wird u um eins verringert. Wenn M [l] + M [u] < r, dann wird l um eins vergrößert. Sollte zu irgendeinem Zeitpunkt u < l gelten, wird abgebrochen und für r nein ausgegeben. Diese Schleife betrachtet jedes Element von M maximal einmal, entsprechend läuft die Schleife in O(n). Da es n Elemente r ∈ M gibt, ergibt sich eine Gesamtlaufzeit 2 von O n . Der Pseudocode dazu könnte so aussehen: A := Array of N umbers Function isNumberComposite(n : N) : l := 1 u := A.length while u > l do if A[l] + A[u] = n then return Yes elsif A[l] + A[u] < n then u−− else l++ endif return No – – sorted ascendingly – – Points to smallest currently still possible element – – Points to largest currently still possible element foreach n in A do isNumberComposite(n) 4 5