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 2. Ü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 (Rekurrenzen, 3 + 3 Punkte) a) Gegeben sei folgende Rekurrenz: ( T (n) = 1 9·T n 3 + 7n für n < 3, für n ≥ 3 Zeigen Sie durch vollständige Induktion, dass T (n) ≤ 47n2 − 4n gilt, falls sich n als n = 3k oder n = 2 · 3k (mit k ∈ N0 ) schreiben lässt. Musterlösung: Der Trick ist: Bei Zahlen, die sich wie oben beschrieben schreiben lassen, landet man bei wiederholtem Teilen durch 3 irgendwann entweder bei 1 oder bei 2. Also müssen zwei Induktionsanfänge gezeigt werden: Einmal für n = 1, einmal für n = 2. Induktionsanfang n = 1: T (1) = 1 ≤ 47 · 12 − 4 · 1 = 43 Induktionsanfang n = 2: T (2) = 1 ≤ 47 · 12 − 4 · 1 = 43 Induktionsvoraussetzung: T (n) ≤ 47n2 − 4n gilt für ein n (mit n = 3k oder n = 2 · 3k ) Induktionsschluss n 3n: T (3n) = 9 · T 3n 3 + 7 · (3n) IV ≤ 9(47n2 − 4n) + 7 · (3n) = 47(3n)2 − 36n + 21n = 47(3n)2 − 5 · (3n) ≤ 47 · (3n)2 − 4 · (3n) b) Gegeben sei folgende Rekurrenz: c0 n l m T (n) = T ( n ) + T ( 5 n + 3 ) + c1 n 4 12 2 falls n ≤ 20, falls n > 20. Finden Sie eine Funktion f , so dass T (n) ∈ Θ(f (n)) gilt und beweisen Sie Ihre Behauptung. 1 Musterlösung: Offensichtlich gilt T (n) ∈ Ω (n) aufgrund der Terme c0 n bzw. c1 n (wir nehmen hierbei c0 , c1 > 0 an, genau genommen fehlt dieser Zusatz in der Aufgabenstellung). Wenn wir nun noch T (n) ∈ O(n) zeigen können, haben wir die gewünschte Aussage für f : N → N, n 7→ n gezeigt. Dazu zeigen wir induktiv: Es sei c:= max{c0 , 12c1 }. Dann gilt für alle n ∈ N die Aussage T (n) ≤ cn. Induktionsanfang n ≤ 20: T (n) = c0 n ≤ cn. Induktionsvoraussetzung: Für ein n ∈ N gilt T (k) ≤ ck ∀k < n. Induktionsschluss: 5 3 n ) + T( n + ) + c1 n 4 12 2 IV 5 n 3 +c + c1 n ≤c n+ 4 12 2 n 5 3 1 ≤c +1 +c n + + 1 + cn 4 12 2 12 n>20 3 7 1 7 = cn + c = cn − cn + c ≤ cn 4 2 4 2 T (n) = T ( Aufgabe 2 (Master-Theorem, 4 Punkte) Zeigen Sie mit Hilfe der gerundeten Version des Master-Theorems scharfe asymptotische Schranken für folgende Rekurrenzen: a) A(1) = 1 und für n ∈ N: A(n) = 5A(dn/5e) + 12n √ b) B(1) = 6 und für n ∈ N: B(n) = 8B(dn/2e) + 4n + 3 n + 17 c) C(1) = 12 und für n ∈ N: C(n) = C(dn/7e) + n d) D(1) = 5 und für n ∈ N: D(n) = 7D(dn/3e) + C(n) + 7n Musterlösung: Master-Theorem (gerundete Version): Für positive Konstanten a, b, c, d und n ∈ N sei ( T (n) = a falls n = 1 Basisfall dT (dn/be) + cn falls n > 1 teile und herrsche. Dann gilt T (n) ∈ Θ(n) falls d < b falls d = b Θ nlogb d falls d > b. Θ(n log n) d=b a) a = 1, b = 5, c = 12, d = 5 ⇒ A(n) ∈ Θ(n log n) √ b) Für n ≥ 1 : 4n ≤ 4n + 3 n + 17 ≤ 24n. Definiere B1 (1) = 6, B1 (n) = 8B1 (n/2) + 4n und B2 (1) = 6, B2 (n) = 8B2 (n/2) + 24n. Dann gilt B1 (n) ≤ B(n) ≤ B2 (n). Immer gilt a = 6, b = 2, d>b d>b d = 8. Mit c = 4 ⇒ B1 (n) ∈ Θ n3 und mit c = 24 ⇒ B2 (n) ∈ Θ n3 zusammen ergibt sich B(n) ∈ Θ n3 . d<b c) a = 12, b = 7, c = 1, d = 1 ⇒ C(n) ∈ Θ(n) 2 d) Da C(n) ∈ Θ(n) existieren c1 , c2 , n0 ∈ N, sodass c1 n ≤ C(n) ≤ c2 n für alle n ≥ n0 . Definiere also D1 (1) = 5, D1 (n) = 7D1 (n/3) + (c1 + 7)n und D2 (1) = 5, D2 (n) = 7D2 (n/3) + (c2 + 7)n. Dann gilt D1 (n) ≤ D(n) ≤ D2(n) für alle n ≥ n0 . Immer gilt a = 5, b = 3, d = 7. Mit d>b d>b c = (c1 + 7) ⇒ D1 (n) ∈ Θ nlog3 7 und mit c = (c2 + 7) ⇒ D2 (n) ∈ Θ nlog3 7 zusammen ergibt sich D(n) ∈ Θ nlog3 7 . Aufgabe 3 (O-Kalkül, 3 Punkte) Sortieren Sie die folgenden Funktionen von der asymptotisch kleinsten zur asymptotisch größten. Schreiben Sie f (n) g(n), falls f (n) ∈ o(g(n)) und f (n) ≡ g(n) falls f (n) ∈ Θ(g(n)). Es werden keine Beweise benötigt. n! logn n log n n1,001 π log log1000 n 10n n √ n nn n n/ log n n log n nlog n Musterlösung: √ π ≡ logn n log n ≡ log1000 n n/ log n n n log n n1,001 nlog n log n n 10n n! nn (Für jede richtige Zuordnung 0, 25 Punkte.) Aufgabe 4 (Schleifeninvarianten, 6 Punkte) Für zwei gegebene ganze Zahlen a, b ∈ N0 heißt eine Zahl m ∈ N0 der größte gemeinsame Teiler (greatest common divisor, GCD) von a und b, wenn (i) m gemeinsamer Teiler von a und b ist, in Zeichen m | a und m | b, und (ii) für jeden gemeinsamen Teiler m0 ∈ N0 aus m0 | a und m0 | b unmittelbar m0 | m folgt. Der Euklidische Algorithmus berechnet zu zwei gegebenen Zahlen a, b ∈ N0 den größten gemeinsamen Teiler. Wir betrachten im Folgenden drei Varianten des Grundalgorithmus. Zeigen Sie für jede der drei Varianten die Korrektheit, indem sie an den benötigten Stellen Assertions und Invarianten einfügen und diese beweisen. Falls notwendig, fügen Sie Hilfsvariablen ein oder annotieren Sie Variablen mit Indizes, um den entsprechenden Schleifendurchlauf zu bezeichnen. a) Übersetzt man die alten Schriften von Euklid in heutigen Pseudocode, dann sieht dieser Algorithmus wie folgt aus: Function euklid(a : N+ ; b : N+ ) : N+ while a 6= b do if a > b then swap(a, b) b := b − a return a Musterlösung: a) Wir zeigen zuerst gcd(a, b) = gcd(a, b − a) für a ≤ b. Sei m = gcd(a, b) der GCD von a und b, nach Definition gilt also m | a, m | b, und für alle m0 ∈ N0 mit m0 | a und m0 | b folgt m0 | m. Es gibt also za ∈ N0 und zb ∈ N0 , so dass a = za m und b = zb m. So gilt b − a = (zb − za )m, und daher auch m | b − a. Wegen m | a und m | b − a ist m also gemeinsamer Teiler von a und b − a. 3 Bleibt zu zeigen dass m größter gemeinsamer Teiler von b − a ist. Sei also m̃ = gcd(a, b − a) ∈ N0 . Wegen m̃ | a und m̃ | b − a gilt a = z̃a m̃ und b − a = z̃b−a m̃ für zwei z̃a , z̃b−a ∈ N0 , und so b − a + a = (z̃b−a + z̃a )m̃ = b. Es gilt also m̃ | b, und da auch m̃ | a, folgt m̃ | m aus der Definition von m. Entsprechend folgt aus m | a und m | b − a auch m | m̃. Damit ist dann m = m̃. Weiter gilt gcd(a, b) = gcd(b, a), was wir hier nicht zeigen. Mit diesen Lemmata können wir folgende Invarianten eintragen: Function euklid(a : N+ ; b : N+ ) : N+ while a 6= b do (a00 , b00 ) = (a, b) if a > b then swap(a, b) invariant a < b (a0 , b0 ) = (a, b) b := b − a invariant gcd(a, b) = gcd(a, b0 − a) = gcd(a0 , b0 − a0 ) = gcd(a0 , b0 ) = gcd(a00 , b00 ). invariant a ≤ a0 und b < b0 return a In jedem Schleifendurchlauf bleibt gcd(a, b) konstant. Der Algorithmus terminiert da 1 ≤ |a + b| < |a0 + b0 |, die Variablen also immer kleiner werden. Bei Abbruch ist a = b = gcd(a, b) = gcd(a, a). b) Die wiederholten Subtraktionen kann man durch eine einzige Division mit Rest ersetzen: Function euklidWithModulo(a : N; b : N) : N if a < b then swap(a, b) while b 6= 0 do (a, b) := (b, a mod b) return a Musterlösung: b) Wieder zeigen wir zuerst gcd(a, b) = gcd(a mod b, b) für a ≥ b. Wir verwenden dazu gcd(a, b) = gcd(a − b · k, b) für b · k ≤ a. Dies kann man einfach per Induktion aus gcd(a, b) = gcd(a − b, b) für a ≥ b folgern. Ist a ≥ b, dann liefert Division mit Rest, ausgedrückt als a = b · (a div b) + (a mod b), die Gleichung a mod b = a − b · (a div b). Hierbei ist a div b := k eine Ganzzahl und bk ≤ a ist ebenfalls erfüllt. Damit kann man das vorherige Lemma auf gcd(a mod b, b) anwenden, und erhält gcd(a mod b, b) = gcd(a − b · (a div b), b) = gcd(a − bk, b) = gcd(a, b). Function euklidWithModulo(a : N; b : N) : N if a < b then swap(a, b) assert a ≥ b while b 6= 0 do (a0 , b0 ) = (a, b) (a, b) := (b, a mod b) invariant gcd(a, b) = gcd(b0 , a0 mod b0 ) = gcd(a0 mod b0 , b0 ) = gcd(a0 , b0 ) . invariant a = b0 und b < a0 return a Wieder bleibt in jedem Schleifendurchlauf gcd(a, b) konstant. Der Algorithmus terminiert da 1 ≤ |a + b| < |a0 + b0 |, die Variablen also immer kleiner werden. Bei Abbruch ist a = gcd(a, b) = gcd(a, a). 4 c) In der Kryptographie ist der sogenannte Erweiterte Euklidische Algorithmus von besonderer Bedeutung. Er berechnet neben dem GCD noch zwei Zahlen s, u ∈ Z, sodass sich m = gcd(a, b) schreiben lässt als m = a · s + b · u. Die Idee hinter der Erweiterung besteht darin bei der Division mit Rest, die ganzzahligen Vielfache, a div b, nicht zu verwerfen, sondern in s und u zu sammeln. Die Zahlen s und u kann man nach Ausführen von euklidWithModulo() durch Rückeinsetzen der Divisionen errechnen, oder, noch geschickter, währenddessen: Function extendedEuklid(a : N; b : N) : (N, Z, Z) (s, t) := (1, 0) (u, v) := (0, 1) while b > 0 do q := a div b (a, b) := (b, a − qb) (s, t) := (t, s − qt) (u, v) := (v, u − qv) return (a, s, u) Hinweis: Es gilt gcd(a, 0) = gcd(0, a) = a. Musterlösung: c) Wir versehen zuerst die Variablen im Pseudocode mit Indizes, die den jeweiligen Schleifendurchlauf bezeichnen: Function extendedEuklid(a : N; b : N) : (N, Z, Z) (s0 , t0 ) := (1, 0) (u0 , v0 ) := (0, 1) i := 0 while bi > 0 do qi := ai div bi (ai+1 , bi+1 ) := (bi , ai − qi bi ) invariant gcd(ai+1 , bi+1 ) = gcd(bi , ai − (ai div bi )bi ) = gcd(bi , ai mod bi ) = gcd(bi , ai ) (si+1 , ti+1 ) := (ti , si − qi ti ) (ui+1 , vi+1 ) := (vi , ui − qi vi ) i++ return (ai , si , ui ) Die erste Invariante zeigt dasselbe wie in Teilaufgabe (b), nämlich dass sich der gcd(a, b) nicht ändert. Die verwendeten Argumente sind die gleichen wie bei (b), nur dass die Modulo Operation mit Hilfe des ganzzahligen Vielfachen qi berechnet wird. Durch die Indizierung erhalten wir folgende rekursiven Gleichungen: ai+1 = bi (1) bi+1 = ai − qi bi = ai mod bi si+1 = ti (2) ti+1 = si − qi ti (5) ui+1 = vi (3) vi+1 = ui − qi vi (6) Mit zweischrittigen Induktion zeigen wir zuerst ai = si a0 + ui b0 . Induktionsanfänge: 5 (4) qi = ai div bi (7) i = 0 : s0 a0 + u0 b0 = 1 · a0 + 0 · b0 = a0 X. (1) (2,3) i = 1 : s1 a0 + u1 b0 = t0 a0 + v0 b0 = 0 · a0 + 1 · b0 = b0 = a1 X. Nun der Induktionsschritt von (i − 1, i) auf (i + 1). Wir dürfen also die Gleichung für i − 1 und i annehmen um i + 1 zu zeigen: (2,3) si+1 a0 + ui+1 b0 = ti a0 + vi b0 (5,6) = (si−1 − qi−1 ti−1 )a0 + (ui−1 − qi−1 vi−1 )b0 = si−1 a0 − qi−1 ti−1 a0 + ui−1 b0 − qi−1 vi−1 b0 = si−1 a0 + ui−1 b0 − qi−1 (ti−1 a0 + vi−1 b0 ) (2,3) = si−1 a0 + ui−1 b0 − qi−1 (si a0 + ui b0 ) I.A. = i und i+1 ai−1 − qi−1 ai (1) (4) (1) = ai−1 − qi−1 bi−1 = bi = ai+1 Damit ist die Gleichung gezeigt. Diese lässt sich auch unmittelbar in eine zweite Invariante umformen: (1,2,3) ai+1 = si+1 a0 + ui+1 b0 ⇐⇒ bi = ti a0 + vi b0 . Mit den beiden Lemmata erhalten wird folgende beiden Invarianten: Function extendedEuklid(a : N; b : N) : (N, Z, Z) (s0 , t0 ) := (1, 0) (u0 , v0 ) := (0, 1) i := 0 while bi > 0 do invariant ai = si a0 + ui b0 und bi = ti a0 + vi b0 . qi := ai div bi (ai+1 , bi+1 ) := (bi , ai − qi bi ) invariant gcd(ai+1 , bi+1 ) = gcd(bi , ai − (ai div bi )bi ) = gcd(bi , ai mod bi ) = gcd(bi , ai ) (si+1 , ti+1 ) := (ti , si − qi ti ) (ui+1 , vi+1 ) := (vi , ui − qi vi ) i++ return (ai , si , ui ) Da am Ende ai = gcd(a, b) = si a0 + ui b0 gilt, ist die Korrektheit gesichert. Der Algorithmus terminiert auch, da für i ≥ 1 immer bi+1 = ai − qi bi = ai − (ai div bi )bi = ai mod bi < bi gilt. Für i = 0 kann in der ersten Iteration qi = 0 sein. Die Kette (bi ) steigt also ab, bis der Algorithmus terminiert mit ai mod bi = 0. Aufgabe 5 (Graphen, 3 Punkte) Der ebenso brilliante wie einflussreiche Wissenschaftler und Superbösewicht Doktor Meta hat sich ein internationales Netzwerk von Helfern und hochrangigen Kontakten aufgebaut. In diesem Netzwerk ist er nicht mit allen persönlich bekannt, baut aber darauf, seinen Einfluss auch über Mittelspersonen ausüben zu können. Da er von Grund auf höchst misstrauisch ist und mögliche Kollaborationen gegen seine Person verhindern möchte, hat er peinlich genau darauf geachtet, dass es im Netzwerk keine drei 6 Personen gibt, die sich alle direkt kennen. Gleichzeitig ist er für ein stabiles Netzwerk an einer möglichst hohe Dichte der Bekanntschaften interessiert. Zeigen Sie, dass in diesem Szenario bei 2n Personen im Netzwerk (inklusive Doktor Meta) höchstens n2 Bekanntschaften möglich sind. Zeigen sie weiterhin, dass es für jedes n ∈ N auch ein Netzwerk mit n2 Bekanntschaften gibt, das diese Bedingung erfüllt. Musterlösung: Das Netzwerk kann als ungerichteter Graph modelliert werden, wobei die Knoten den Personen und eine Kante der Bekanntschaft zwischen zwei Personen entspricht. Zunächst zeigen wir, dass sich die Grenze erreichen lässt: Man teilt die 2n Knoten in zwei Mengen A, B mit jeweils n Knoten. Dann fügt man für jedes a ∈ A eine Kante zu jedem Knoten b ∈ B ein. Damit hat man n2 Kanten eingefügt. Jede Menge von drei Knoten enthält mindestens zwei Knoten in A oder mindestens zwei Konten in B. Da zwischen zwei Knoten in A oder zwischen zwei Knoten in B nie eine Kante existiert, ist der Graph dreiecksfrei. Die obere Schranke zeigen wir mit Induktion: Die Aussage gilt offensichtlich für n = 1. Sei die Aussage nun wahr für einen Graphen mit 2n Knoten, dann zeigen wir sie nun für einen Graphen mit 2n + 2 Knoten. Sei G = (V, E) ein dreiecksfreier Graph mit 2n + 2 Knoten. Betrachte nun eine Kante {u, v}. Da G dreiecksfrei ist, gilt für jeden Knoten w ∈ V \ {u, v}, dass entweder die Kante {u, w} oder die Kante {v, w} oder keine von beiden in G existiert. Daher gibt es maximal 2n Kanten zwischen u und v einerseits und Knoten w ∈ V \ {u, v} andererseits. Sei G0 der Graph, der entsteht, wenn man u und v und alle zu ihnen inzidenten Kanten aus G löscht. G0 hat maximal 2n + 1 Kanten weniger als G. Da G dreiecksfrei ist, ist auch G0 dreiecksfrei, und damit hat G0 nach Induktionsannahme maximal n2 Kanten. Damit hat G maximal n2 + 2n + 1 = (n + 1)2 Kanten. Daraus folgt die Behauptung. 7