Size: 8196
Comment:
|
Size: 8664
Comment:
|
Deletions are marked like this. | Additions are marked like this. |
Line 1: | Line 1: |
#acl -All:read == AUFGABE 1 (Diverses) == === 1.1 log n = O(log^2 n) aber nicht Theta === Sei log der Logarithmus zur Basis 2. (Wir können die Basis beliebig wählen, weil sich log,,b1,, und log,,b2,, nur um einen (positiven) konstanten Faktor ln b1 / ln b2 untertscheiden, für b1, b2 > 1. Für alle n >= 2 ist log n >= 2 und damit, log n <= (log n)^2^, also gilt O mit n,,0,, = 2 und C = 1. Falls Theta, müsste auch Omega sein, dann würde es C, n,,0,, geben, so dass für alle n >= n,,0,, gilt, dass log n >= C * log^2^ n. Dann wäre aber log n <= 1/C für alle n >= n,,0,,. Aber z.B. für n = max(n,,0,,, 2^1 + 1/C^) ist n >= n,,0,, und log n > 1/C. Es reicht auch als Argument, dass log n für hinreichend große n beliebig groß wird (wenn auch langsam). === 1.2 Was macht das Programm das da steht === Es berechnet die Anzahl der ''verschiedenen'' Elemente im Eingabefeld. Begründung: zu Beginn nimmt man an, dass alle Elemente verschieden sind, dann wäre diese Anzahl = #Größe des Feldes. Dann geht man von links nach rechts durch, und immer wenn man ein Element findet, dass man schon mal gesehen hat, zieht man 1 ab. === 1.3 Laufzeit im besten und schlechtesten Fall === Bester Fall: Theta(n). Begründung: das passiert z.B. wenn alle Elemente gleich sind. Dann bricht die innere Schleife immer schon nach dem ersten Durchlauf ab. Schneller als Theta(n) geht nicht, weil die äußere Schleife immer n mal durchläuft. Schlechtester Fall: Theta(n^2^). Begründung: das passiert z.B. wenn alle Elemente verschieden sind. Dann läuft die innere Schleife immer ganz zum Ende und die Laufzeit ist proportional zu sum_{i=1,...,n} i = Theta(n^2^). == AUFGABE 2 (Hashing) == === 2.1 Zustand der Hashtabelle nach Einfügen von 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 === Für h(x) = x^2^ mod 5 ist: h(1) = 1, h(2) = 4, h(3) = 4, h(4) = 1, h(5) = 0, h(6) = 1, h(7) = 4, h(8) = 4, h(9) = 1, h(10) = 0. Zustand der Hashtabelle also: {{{ 0 : 5, 10 1 : 1, 4, 6, 9 2 : nix 3 : nix 4 : 2, 3, 7, 8 }}} === 2.2 Beweis, dass h(x + 5) = h(x) === {{{ h(x+5) = (x+5)^2 mod 5 = (x^2 + 10x + 5) mod 5 = (x^2 mod 5) + (10x mod 5) + (5 mod 5) = x^2 mod 5 = h(x) }}} Alternativ: {{{ h(x+5) = (x+5)^2 mod 5 = (((x+5) mod 5) * ((x+5) mod 5)) mod 4 = ((x mod 5) * (x mod 5)) mod 5 = x^2 mod 5 = h(x) }}} === 2.3 Pr(h(x) = i) für i = 0, 1, 2, 3, 4 === Nach 2.2 ist die Verteilung für einen zufälligen Schlüssel aus N dieselbe wie für einen zufälligen Schlüssel aus {1, 2, 3, 4, 5}. Und dafür ist: {{{ Pr(h(x) = 0) = 1/5 ... nur die 5 wird auf 0 abgebildet Pr(h(x) = 1) = 2/5 ... 1 und 4 werden auf 1 abgebildet Pr(h(x) = 2) = 0 ... nix wird auf 2 abgebildet Pr(h(x) = 3) = 0 ... nix wird auf 3 abgebildet Pr(h(x) = 4) = 2/5 ... 2 und 3 werden auf 4 abgebildet }}} === 2.4 Additivität von Universalität === Sei m die Größe der Hashtabelle und x, y ein beliebiges Schlüsselpaar mit x != y. Dann {{{ H1 c-universell => |{h in H1 : h(x) = h(y)|| <= c * |H1| / m H2 c-universell => |{h in H2 : h(x) = h(y)|| <= c * |H2| / m }}} Da H1 und H2 disjunkt ist ein h aus H entweder in H1 oder in H2, und |H| = |H1| + |H2|, und somit {{{ |{h in H : h(x) = h(y)}| = |{h in H1 : h(x) = h(y)}| + |{h in H2 : h(x) = h(y)}| <= c * |H1| / m + c * |H2| / m = c * |H| / m. }}} Und damit ist H auch c-universell. == AUFGABE 3 (Dynamische Felder) == === 3.1 Wann i-te Reallokation wenn c1 = 1 und immer Verdoppelung === Dann Größe nach i-ter Reallokation 2^i. Induktionsanfang i = 1: erste Reallokation beim Einfügen des zweiten Elemente. Größe vor der Reallokation 1, danach 2 = 2^1^. Induktionsschritt i -> i + 1: Nach Induktionsvoraussetzung ist die Kapazität nach der i-ten Reallokation 2^i^. Es wird also das nächste Mal beim Einfügen des (2^i^ + 1)-sten Elementes wieder realloziert, und dann auf 2 * 2^i^ = 2^i+1^. === 3.2 Gesamtanzahl Blockoperationen bei Einfügen von n Elementen === {{{ #IOs bis zur ersten Reallokation: Theta(1) #IOs von der i-ten Reallokation: Theta(2^i / B) #IOs ab da bis zur (i+1)-ten Reallokation: Theta(2^i / B) #IOs insgesamt also Theta(sum_{i=1}^k 2^i / B) = Theta(2^k / B) = Theta(n / B), k = #Anzahl Reallokationen }}} == AUFGABE 4 (a,b-Bäume) == === 4.1 (2,3)-Baum nach Einfügen von 1, 2, 3, 4, 5, 6 === {{{ 2|4|6 / | \ 1|2 3|4 5|6 / \ / \ / \ 1 2 3 4 5 6 }}} === 4.2 Wann zum ersten Mal Tiefe 3 === Nach dem Einfügen von 8. Begründung: dann bekommt die Wurzel zum ersten Mal 4 Kinder und muss aufgespalten werden. === 4.3 Wann zum ersten Mal Tiefe 4 === Nach dem Einfügen von 16. Begründung: der Baum für die Knoten 9, 10, 11, 12, 13, 14, 15, 16 sieht dann so aus wie der Baum für 1, 2, 3, 4, 5, 6, 7, 8 und die (nach 4.2 neue) Wurzel bekommt dann zum ersten Mal wieder vier Kinder und muss aufgespalten werden. === 4.4 Amortisierte Kosten für n beliebige Operationen === Theta(n * log n), siehe [[http://ad-teaching.informatik.uni-freiburg.de/AlgoDatSS2013/vorlesung-8b.pdf#page=12|Vorlesung 8b, Folie 12]]. == AUFGABE 5 (Graphen) == === 5.1 Dijkstra von S nach Z === Hier erst nochmal der Graph mit Buchstaben als Knotennamen (A, B, C, D, E, F, G, H, J). Startknoten ist A, Zielknoten ist G (nicht J). {{{ A --1-- B --1-- C | | | 2 2 2 | | | D --1-- E --1-- F | | | 2 2 2 | | | G --1-- H --1-- J }}} Dann macht Dijkstra Folgendes: {{{ Runde 1: dist[A] = 0 gelöst, dist[B] = 1, dist[D] = 2 Runde 2: dist[B] = 1 gelöst, dist[C] = 2 neu, dist[E] = 3 neu, dist[D] = 2 unverändert Runde 3: dist[D] = 2 gelöst, dist[G] = 4 neu, dist[C] = 2 und dist[E] = 3 unverändert Runde 4: dist[C] = 2 gelöst, dist[F] = 4 neu, dist[G] = 4 und dist[E] = 3 unverändert Runde 5: dist[E] = 3 gelöst, dist[H] = 5 neu, dist[F] = 4 und dist[G] = 4 unverändert Runde 6: dist[G] = 4 gelöst. }}} Bemerkung 1: man kennt den richtigen Wert für dist[G] zwar schon in Runde 4, aber aufhören darf man erst in Runde 6. Da sollte man dann aber auch aufhören. Bemerkung 2: Man hat an zwei Stellen die Auswahl zwischen zwei Knoten für die nächste Runde. In Runde 3 könnte man statt D auch C lösen (beide dist = 2). In Runde 6 könnte man statt G auch erst F lösen (beide dist = 4), dann würde G erst in Runde 7 gelöst. === 5.2 Programm zur Berechnung des Durchmessers === {{{ void computeDiameter(Graph G) { int maxSoFar = -INFINITY; for (int i = 0; i < G.size(); i++) { int[] dist = computeDijkstra(G, i); for (int j = 0; j < G.size(); j++) { if (dist[j] != INFINITY && dist[j] > maxSoFar) { maxSoFar = dist[j]; } } } return maxSoFar; } }}} == AUFGABE 6 (String Matching) == === 6.1 Knuth-Morris-Pratt für T = BANANANENA und P = NANA === Vorberechnung für das Muster (Positionen hier 1 bis 4, nicht 0 bis 3, kann man aber machen wie man will): {{{ 1234 NANA 0012 }}} Ausführung des Algorithmus (Positionen hier 1 bis 10, nicht 0 bis 9, kann man aber machen wie man will): {{{ 1234567890 BANANANENA N N NANA ... MATCH an Positionen 3 bis 6 in T, weiter in T ab Stelle 7 - P[4] = 5 NANA ... MISMATCH an Position 8 in T / 4 in P, weiter in T ab Stelle 8 - P[3] = 7 NA ... MISMATCH an Position 8 in T / 2 in P, weiter in T ab Stelle 8 - P[1] = 8 N ... ENDE, weil < |P| Positionen in T übrig }}} === 6.2 Anzahl Vergleiche des trivialen Algorithmus === 14 Vergleiche, wenn der Algorithmus aufhört wenn <|P| Positionen in T übrig. Sonst 18 Vergleiche. Beide Lösungen OK. {{{ 1234567890 BANANANENA N 1 N 1 NANA 4 N 1 NANA 4 N 1 NA 2 ------------- N 1 NA 2 N 1 }}} === 6.3 Maximal viele Vergleiche beim trivialen Algorithmus === T = a...a (n mal), P = a...a (m mal). Dann an jeder Position m Vergleiche (mehr geht nicht). Insgesamt Theta(n * m). |
#acl -All:read Sabine Storandt:read,write Björn Buchhold:read,write Claudius Korzen:read,write Elmar Haußmann:read,write == AUFGABE 1 (Programmm + Laufzeitbestimmung) == === 1.1 Funktion zur Berechnung der zweitgrößten Zahl in einem Feld der Größe >= 2 === {{{ def secondLargest(A): max1 = max(A[0], A[1]) max2 = min(A[0], A[1]) for i in range(2, len(A)): if A[i] > max1: max2 = max1 max1 = A[i] else if A[i] > max2: max2 = A[i] return max2 }}} === 1.2 Laufzeit rekursive Berechnung Fibonacci-Zahlen === Es gilt offensichtlich T,,n,, >= T,,n-1,, + T,,n-2,,. Induktionsanfang: T_1 >= 2 und T_2 >= 1 gilt offensichtlich. Induktionschritt: Nach Induktionsvoraussetzung ist T,,n-1,, >= F,,n-1,, und T,,n-2,, >= F,,n-2,,. Damit T,,n,, >= T,,n-1,, + T,,n-2,, >= F,,n-1,, + F,,n-2,, = F,,n,,. === 1.3 Funktion zur Berechnung von F_n in Zeit O(n), für n >= 2 === {{{ def fastFibonacci(n): fibs = [1] * n for i in range(2, n): fibs[i] = fibs[i - 1] + fibs[i - 2] return fibs[n-1] }}} == AUFGABE 2 (Felder + Hashing) == === 2.1 Funktion zum Suchen und Löschen eines Elementes aus einem Feld === {{{ def removeFromArray(A, x): i = 0 # Find the element. while i < len(A) and A[i] != x: i = i + 1 # If found, swap to the end and remove last element. if i < len(A): A[i], A[-1] = A[-1], A[i] A.pop() }}} === 2.2 Funktion zum Löschen eines Elementes aus einer Hashtabelle === {{{ def removeFromHashTable(T, h, x): removeFromArray(T[h(x)], x) }}} === 2.3 Laufzeit der Funktionen aus Aufgabe 2.1 und 2.2 === Die Funktion aus Aufgabe 2.1 hat Laufzeit Theta(i), wenn das Element an Stelle i steht bzw. Theta(n) wenn das Element nicht im Feld steht. Die Funktion aus Aufgabe 2.2 hat Laufzeit Theta(1) im besten Fall (unter h(x) stehen in T nur konstant viele Elemente) und Laufzeit Theta(n) im schlechtesten Fall (alle Elemente in T werden auf h(x) abgebildet und das gesuchte Element ist nicht dabei oder steht ganz am Ende des Feldes). === 2.4 Klasse H nicht 1-universell, wenn |H| < m und |U| > m === Nach Definition von 1-universell müsste für alle x, y mit x != y gelten, dass |{h: h(x) = h(y)}| <= c * |H| / m < 1, also = 0. Das heißt, für jedes Schlüsselpaar x, y mit x != y darf es keine Hashfunktion geben, unter der die beiden Schlüssel auf dasselbe abgebildet werden. Für jede Hashfunktion h muss es aber zwei Schlüssel geben, die auf dasselbe abgebildet werden, weil es > m Schlüssel gibt aber nur m Plätze in der Hashtabelle. == AUFGABE 3 (Binäre Heaps) == === 3.1 Heapzustand (als Baum und als Feld) nach dem Einfügen von 5, 4, 3, 2, 1 (in der Reihenfolge) === Von links nach rechts: oben = Baum, unten = Feld (x = beliebiger Wert an Position 0 im Feld) {{{ 5 4 3 2 1 / / \ / \ / \ 5 5 4 3 4 2 4 / / \ 5 5 3 x5 x45 x354 x2345 x12453 }}} === 3.2 Funktion zur Überprüfung der Heapeigenschaft === {{{ def checkHeapProperty(heap): for i in range(2, len(heap)): child_value = heap(i) parent_value = heap(i/2) if child_value < parent_value: return False return True }}} === 3.3 Laufzeit der Funktion aus Aufgabe 3.2 === Die Laufzeit is Theta(n). Begründung: die Schleife läuft n - 2 = Theta(n) mal durch und in jedem Durchlauf gibt es eine konstante Anzahl von Anweisungen. === 3.4 Anzahl Blockoperationen der Funktion aus Aufgabe 3.2 === Die Anzahl Blockoperation is Theta(n/B). Begründung: für das child_value liest man n Elemente von links nach rechts, eins nach dem anderen, das sind ~ n/B Blockoperationen. Für das parent_value liest man n/2 Elemente von links nach rechts, eins nach dem anderen (und immer denselben Wert zweimal hintereinander), das sind ~ (n/2)/B Blockoperationen. == AUFGABE 4 (Graphen) == === 4.1 Zeichnung des Graphen + Darstellung durch Adjazenzlisten und Adjazenmatrix === Die Zeichnung ist trivial (ein Dreick, mit den Kanten einmal reihum, Gewicht jeweils 2). Darstellung durch Adjazenzlisten (u,,1,, -> 0, u,,2,, -> 1, u,,3,, -> 2): {{{ 0 -> (1, 2) 1 -> (2, 2) 2 -> (0, 2) }}} Darstellung durch 3 x 3 Adjazentmatrix (x = spezieller Eintrag, der bedeutet "keine Kante"): {{{ x 2 x x x 2 2 x x }}} === 4.2 Funktion zur Berechnung der Anzahl Kanten mit Adjazenzlisten === {{{ # graph is represented as an array of n adjacency lists, where n = #nodes. def numEdges(graph): count = 0 for i in range(0, len(graph)): count += len(graph[i]) return count }}} === 4.3 Funktion zur Berechnung der Anzahl Kanten mit Adjazenzmatrix === {{{ # graph is represented as an array of n array, each of size n, where n = #nodes. def numEdges(graph): count = 0 n = len(graph) for i in range(0, n): for j in range(0, n): if graph[i][j] > 0: count += 1 return count }}} === 4.4 Laufzeit der Funktionen aus Aufgabe 4.2 und Aufgabe 4.3 === Die Laufzeit der Funktion aus Aufgabe 4.2 ist Theta(n), wobei n die Anzahl der Knoten ist. Begründung: die Schleife wird n mal durchlaufen, und in jedem Schleifendurchlauf werden nur konstant viele Anweisungen ausgeführt. Die Laufzeit der Funktion aus Aufgabe 4.3 ist Theta(n^2^), wobei n die Anzahl der Knoten ist. Begründung: die äußere Schleife wir n mal durchlaufen und die innere ebenfalls, das gibt insgesamt n^2^ Durchläufe des Schleifenkörpers, der aus konstant vielen Anweisungen besteht. == AUFGABE 5 (Edi-Tier) == === 5.1 Editierdistanz zwischen REGAL und LAGER === {{{ e L A G E R e 0 1 2 3 4 5 R 1 1 2 3 4 4 E 2 2 2 3 3 4 G 3 3 3 2 3 4 A 4 4 3 3 3 4 L 5 4 4 4 4 4 }}} === 5.2 ED(x, y) = n - 1, falls n ungerade und x das Wort y rückwärts ist und alle Buchstaben verschieden === Falls n ungerade ist, ist der mittlere Buchstabe von x und y gleich. Man also x mit n - 1 replace Operationen in y überführen. Das zeigt aber erstmal nur ED(x, y) <= n - 1 (es könnte auch noch mit weniger Operationen gehen). Die Alternative zu einem Replace wäre die benötigten Buchstaben durch Löschen und Einfügen hinzuzufügen. Das hat Kosten 2 und hilft aber immer nur höchstens zwei Buchstaben, ist also auch nicht besser. === 5.3 ED(x, y) = ?, falls n gerade und x das Wort y rückwärts ist und alle Buchstaben verschieden === Falls n gerade ist, haben x und y keinen Buchstaben an derselben Stelle gemeinsam. Man kann aber auf jeden Fall x mit n Replace Operationen in y überführen. Mit demselben Argument wie oben (Einfügen und Löschen hilft höchstens zwei Buchstaben) geht es auch nicht besser === 5.4 ED(x, y) im niedrigsten Fall, wenn nur mindestens zwei verschiedene Buchstaben === Wenn x ein Palindrom ist (z.B. Hannah oder Hannnah), dann ist x = y und ED(x, y) = 0. == AUFGABE 6 (O-Notation, Logarith-Mus, W-keit) == === 6.1 Zeigen Sie, dass 2n + 17 = O(n^2) über die Definition von O mittels C und n,,0,, === Für n >= 5 ist 2n + 17 <= n^2^ + n^2^ <= 2 n^2^. Also Bedingung per Definition erfüllt mit n,,0,, = 5 und C = 2. === 6.2 Wert von log,,2,, (2^64^ * 8^12^) === log,,2,, (2^64^ * 8^12^) = log,,2,, 2^64^ + log,,2,, 8^12^ = 64 + log,,2,, 2^36^ = 64 + 36 = 100. Dabei wurde benutzt dass 8 = 2^3^ und "x hoch y hoch z" gleich "x hoch (y * z)" ist. === 6.3 Wahrscheinlichkeit guter Pivot (beide Teile >= n/4) bei Quicksort, bei zufälliger Wahl === Seien die Elemente ohne Beschränkung der Allgemeinheit 1, ..., n (es zählt nur die Reihenfolge nicht der Wert). Wählt man eines der n/4 Elemente 1, ..., n/4 als Pivot ist der "vordere" Teil (Elemente < Pivot) kleiner als n/4. Wählt man eines der n/4 Elemente n - n/4 + 1, ..., n als Pivot ist der "hintere" Teil (Elemente > Pivot) kleiner als n/4. Wählt man eines der übrigen n/2 Elemente als Pivot, sind beide Teile >= n/4. Die Wahrscheinlichkeit ist also 1/2. === 6.4 Wahrscheinlichkeit von 6.3 bei dreimaliger Wiederholung === Wir betrachten erst das Gegenereignis = selbst für die beste Aufteilung ist ein Teil < n/4. Wenn das passiert, ist für alle drei Aufteilungen (von allen drei Pivots), der kleinste Teil n/4. Nach Aufgabe 6.3 passiert das für jede Aufteilung mit Wahrscheinlichkeit 1/2. Die Wahrscheinlichkeit, dass es für alle drei passiert ist also 1/8. Die gefragt Wahrscheinlichkeit ist also 7/8. |
AUFGABE 1 (Programmm + Laufzeitbestimmung)
1.1 Funktion zur Berechnung der zweitgrößten Zahl in einem Feld der Größe >= 2
def secondLargest(A): max1 = max(A[0], A[1]) max2 = min(A[0], A[1]) for i in range(2, len(A)): if A[i] > max1: max2 = max1 max1 = A[i] else if A[i] > max2: max2 = A[i] return max2
1.2 Laufzeit rekursive Berechnung Fibonacci-Zahlen
Es gilt offensichtlich Tn >= Tn-1 + Tn-2.
Induktionsanfang: T_1 >= 2 und T_2 >= 1 gilt offensichtlich.
Induktionschritt: Nach Induktionsvoraussetzung ist Tn-1 >= Fn-1 und Tn-2 >= Fn-2. Damit Tn >= Tn-1 + Tn-2 >= Fn-1 + Fn-2 = Fn.
1.3 Funktion zur Berechnung von F_n in Zeit O(n), für n >= 2
def fastFibonacci(n): fibs = [1] * n for i in range(2, n): fibs[i] = fibs[i - 1] + fibs[i - 2] return fibs[n-1]
AUFGABE 2 (Felder + Hashing)
2.1 Funktion zum Suchen und Löschen eines Elementes aus einem Feld
def removeFromArray(A, x): i = 0 # Find the element. while i < len(A) and A[i] != x: i = i + 1 # If found, swap to the end and remove last element. if i < len(A): A[i], A[-1] = A[-1], A[i] A.pop()
2.2 Funktion zum Löschen eines Elementes aus einer Hashtabelle
def removeFromHashTable(T, h, x): removeFromArray(T[h(x)], x)
2.3 Laufzeit der Funktionen aus Aufgabe 2.1 und 2.2
Die Funktion aus Aufgabe 2.1 hat Laufzeit Theta(i), wenn das Element an Stelle i steht bzw. Theta(n) wenn das Element nicht im Feld steht.
Die Funktion aus Aufgabe 2.2 hat Laufzeit Theta(1) im besten Fall (unter h(x) stehen in T nur konstant viele Elemente) und Laufzeit Theta(n) im schlechtesten Fall (alle Elemente in T werden auf h(x) abgebildet und das gesuchte Element ist nicht dabei oder steht ganz am Ende des Feldes).
2.4 Klasse H nicht 1-universell, wenn |H| < m und |U| > m
Nach Definition von 1-universell müsste für alle x, y mit x != y gelten, dass |{h: h(x) = h(y)}| <= c * |H| / m < 1, also = 0. Das heißt, für jedes Schlüsselpaar x, y mit x != y darf es keine Hashfunktion geben, unter der die beiden Schlüssel auf dasselbe abgebildet werden. Für jede Hashfunktion h muss es aber zwei Schlüssel geben, die auf dasselbe abgebildet werden, weil es > m Schlüssel gibt aber nur m Plätze in der Hashtabelle.
AUFGABE 3 (Binäre Heaps)
3.1 Heapzustand (als Baum und als Feld) nach dem Einfügen von 5, 4, 3, 2, 1 (in der Reihenfolge)
Von links nach rechts: oben = Baum, unten = Feld (x = beliebiger Wert an Position 0 im Feld)
5 4 3 2 1 / / \ / \ / \ 5 5 4 3 4 2 4 / / \ 5 5 3 x5 x45 x354 x2345 x12453
3.2 Funktion zur Überprüfung der Heapeigenschaft
def checkHeapProperty(heap): for i in range(2, len(heap)): child_value = heap(i) parent_value = heap(i/2) if child_value < parent_value: return False return True
3.3 Laufzeit der Funktion aus Aufgabe 3.2
Die Laufzeit is Theta(n). Begründung: die Schleife läuft n - 2 = Theta(n) mal durch und in jedem Durchlauf gibt es eine konstante Anzahl von Anweisungen.
3.4 Anzahl Blockoperationen der Funktion aus Aufgabe 3.2
Die Anzahl Blockoperation is Theta(n/B). Begründung: für das child_value liest man n Elemente von links nach rechts, eins nach dem anderen, das sind ~ n/B Blockoperationen. Für das parent_value liest man n/2 Elemente von links nach rechts, eins nach dem anderen (und immer denselben Wert zweimal hintereinander), das sind ~ (n/2)/B Blockoperationen.
AUFGABE 4 (Graphen)
4.1 Zeichnung des Graphen + Darstellung durch Adjazenzlisten und Adjazenmatrix
Die Zeichnung ist trivial (ein Dreick, mit den Kanten einmal reihum, Gewicht jeweils 2).
Darstellung durch Adjazenzlisten (u1 -> 0, u2 -> 1, u3 -> 2):
0 -> (1, 2) 1 -> (2, 2) 2 -> (0, 2)
Darstellung durch 3 x 3 Adjazentmatrix (x = spezieller Eintrag, der bedeutet "keine Kante"):
x 2 x x x 2 2 x x
4.2 Funktion zur Berechnung der Anzahl Kanten mit Adjazenzlisten
# graph is represented as an array of n adjacency lists, where n = #nodes. def numEdges(graph): count = 0 for i in range(0, len(graph)): count += len(graph[i]) return count
4.3 Funktion zur Berechnung der Anzahl Kanten mit Adjazenzmatrix
# graph is represented as an array of n array, each of size n, where n = #nodes. def numEdges(graph): count = 0 n = len(graph) for i in range(0, n): for j in range(0, n): if graph[i][j] > 0: count += 1 return count
4.4 Laufzeit der Funktionen aus Aufgabe 4.2 und Aufgabe 4.3
Die Laufzeit der Funktion aus Aufgabe 4.2 ist Theta(n), wobei n die Anzahl der Knoten ist. Begründung: die Schleife wird n mal durchlaufen, und in jedem Schleifendurchlauf werden nur konstant viele Anweisungen ausgeführt.
Die Laufzeit der Funktion aus Aufgabe 4.3 ist Theta(n2), wobei n die Anzahl der Knoten ist. Begründung: die äußere Schleife wir n mal durchlaufen und die innere ebenfalls, das gibt insgesamt n2 Durchläufe des Schleifenkörpers, der aus konstant vielen Anweisungen besteht.
AUFGABE 5 (Edi-Tier)
5.1 Editierdistanz zwischen REGAL und LAGER
e L A G E R e 0 1 2 3 4 5 R 1 1 2 3 4 4 E 2 2 2 3 3 4 G 3 3 3 2 3 4 A 4 4 3 3 3 4 L 5 4 4 4 4 4
5.2 ED(x, y) = n - 1, falls n ungerade und x das Wort y rückwärts ist und alle Buchstaben verschieden
Falls n ungerade ist, ist der mittlere Buchstabe von x und y gleich. Man also x mit n - 1 replace Operationen in y überführen. Das zeigt aber erstmal nur ED(x, y) <= n - 1 (es könnte auch noch mit weniger Operationen gehen). Die Alternative zu einem Replace wäre die benötigten Buchstaben durch Löschen und Einfügen hinzuzufügen. Das hat Kosten 2 und hilft aber immer nur höchstens zwei Buchstaben, ist also auch nicht besser.
5.3 ED(x, y) = ?, falls n gerade und x das Wort y rückwärts ist und alle Buchstaben verschieden
Falls n gerade ist, haben x und y keinen Buchstaben an derselben Stelle gemeinsam. Man kann aber auf jeden Fall x mit n Replace Operationen in y überführen. Mit demselben Argument wie oben (Einfügen und Löschen hilft höchstens zwei Buchstaben) geht es auch nicht besser
5.4 ED(x, y) im niedrigsten Fall, wenn nur mindestens zwei verschiedene Buchstaben
Wenn x ein Palindrom ist (z.B. Hannah oder Hannnah), dann ist x = y und ED(x, y) = 0.
AUFGABE 6 (O-Notation, Logarith-Mus, W-keit)
6.1 Zeigen Sie, dass 2n + 17 = O(n^2) über die Definition von O mittels C und n,,0,,
Für n >= 5 ist 2n + 17 <= n2 + n2 <= 2 n2. Also Bedingung per Definition erfüllt mit n0 = 5 und C = 2.
6.2 Wert von log,,2,, (2^64^ * 8^12^)
log2 (264 * 812) = log2 264 + log2 812 = 64 + log2 236 = 64 + 36 = 100. Dabei wurde benutzt dass 8 = 23 und "x hoch y hoch z" gleich "x hoch (y * z)" ist.
6.3 Wahrscheinlichkeit guter Pivot (beide Teile >= n/4) bei Quicksort, bei zufälliger Wahl
Seien die Elemente ohne Beschränkung der Allgemeinheit 1, ..., n (es zählt nur die Reihenfolge nicht der Wert). Wählt man eines der n/4 Elemente 1, ..., n/4 als Pivot ist der "vordere" Teil (Elemente < Pivot) kleiner als n/4. Wählt man eines der n/4 Elemente n - n/4 + 1, ..., n als Pivot ist der "hintere" Teil (Elemente > Pivot) kleiner als n/4. Wählt man eines der übrigen n/2 Elemente als Pivot, sind beide Teile >= n/4. Die Wahrscheinlichkeit ist also 1/2.
6.4 Wahrscheinlichkeit von 6.3 bei dreimaliger Wiederholung
Wir betrachten erst das Gegenereignis = selbst für die beste Aufteilung ist ein Teil < n/4. Wenn das passiert, ist für alle drei Aufteilungen (von allen drei Pivots), der kleinste Teil n/4. Nach Aufgabe 6.3 passiert das für jede Aufteilung mit Wahrscheinlichkeit 1/2. Die Wahrscheinlichkeit, dass es für alle drei passiert ist also 1/8. Die gefragt Wahrscheinlichkeit ist also 7/8.