===== Forendiskussionen ===== * [[https://fsi.informatik.uni-erlangen.de/forum/thread/8074-Wissensfragen-24-02-2011]] * [[https://fsi.informatik.uni-erlangen.de/forum/thread/8875-AK-24-2-11-Aufgabe-6-Graphen-Dijkstra-Stack]] * [[https://fsi.informatik.uni-erlangen.de/forum/thread/8059-Dynamische-Programmierung]] 3c * [[https://fsi.informatik.uni-erlangen.de/forum/thread/9409-24-02-2011]] ===== Lösungsversuch ===== ==== Aufgabe 1 - Wissensfragen ==== **a)** Falsch, alle Throwables können mit catch abgefangen werden, das heißt auch java.lang.Error und davon abgeleitete Klassen. \\ Ob ein Abfangen sinnvoll ist oder unter Umständen fehlschlagen kann (beispielsweise bei einem OutOfMemoryError unter Umständen denkbar), ist an dieser Stelle nicht gefragt. **b)** Richtig, das Argument für die VM lautet "-ea" (für "enable assertions"). **c)** Richtig, Beweis siehe Vorlesung. **d)** * f ∈ O(log n) * g ∈ O(n log n); Hinweis: Hier ist ein kleiner Fehler in der Aufgabenstellung: Die Variable k in Funktion f muss mit 1 initialisiert sein, dem neutralen Element der Multiplikation. **e)** Richtig, denn ohne wahlfreien Zugriff muss die verkettete Liste bis zum gesuchten Element durchlaufen werden. Dass sie sortiert ist, ändert daran nichts, da besser Suchverfahren wie Binary Search wahlfreien Zugriff voraussetzen. **f)** Falsch, im worst-case werden alle Felder nur genau einmal besucht, die Komplexität liegt also in O(n) **g)** Richtig **h)** Falsch, UML ist nicht Java-spezifisch. In andere Programmiersprachen wie beispielsweise C++ ist die Mehrfachvererbung möglich. **i)** Richtig Datei zum selber Testen: {{:pruefungen:bachelor:aud:rack.java.txt|:pruefungen:bachelor:aud:rack.java.txt}} Zusatzinfo: \\ **Frage:** Habe gelesen, dass man bei Generics nur die Methoden von Object aufrufen kann, hier wird aber .getTitle() aufgerufen. Ist das nicht ein Fehler? \\ **Antwort:** Man kann alle Methoden die der generische Typ hat Aufrufen. Im Fall von waere das in der Tat nur Methoden von Object (weil in Java alle Klassen von Object automatisch erben). Hier steht aber , d.h. es koennen auch Methoden der Klasse Book aufgerufen werden, da der generische Typ von Book erben muss. ==== Aufgabe 2 - Bäume ==== **a)** ==Adjazenzmatrix== ^ ^ A ^ B ^ C ^ D ^ E ^ F ^ G ^ ^ A | - | + | - | - | - | - | + | ^ B | + | - | - | + | + | + | - | ^ C | - | - | - | + | - | - | - | ^ D | - | + | + | - | - | - | - | ^ E | - | + | - | - | - | - | - | ^ F | - | + | - | - | - | - | - | ^ G | + | - | - | - | - | - | - | ==Graphische Darstellung== {{:pruefungen:bachelor:audklausur_ws10_aufgabe2.png}} **b)** ^ A ^ B ^ C ^ D ^ E ^ F ^ G ^ | 0 | 1 | 3 | 2 | 2 | 2 | 1 | **c)** Mengenschreibweise: X = (V, E, r) mit Knotenmenge V , Kantenmenge E und Wurzel r V = {A, B, C, D, E, F, G} E = {[A,B],[A,G], [B,D], [B,E], [B,F], [D,C]} r = A **d)** Zunächst einmal muss man sich klar machen, dass ein Graph genau dann ungerichtet ist, wenn die Adjazenzmatrix sowohl den Eintrag (A,B) als auch einen Eintrag (B,A) aufweist. Das heißt: Die Adjazenzmatrix ist an der Diagonale symmetrisch (vergleiche Adjazenzmatrix oben). Grundlegende Idee für die Funktion ''isUndirected'': \\ Die Matrix komplett durchgehen und überprüfen, ob jeder Eintrag matrix[i][j] mit dem an der Diagonale gespiegeltem Wert matrix[j][i] identisch ist. Sollte das nicht der Fall sein, ist der zugehörige Graph gerichtet. public boolean isUndirected(boolean[][] amx) { for(int i = 0; i < amx.length; i++) { // Zeilen for(int j = 0; j < amx[0].length; j++) { // Spalten if(amx[i][j] != amx[j][i]) return false; } } return true; } Geringfügig optimierte Version, die nur eine Dreiecksmatrix durchiteriert: public boolean isUndirected(boolean[][] amx) { for(int i = 0; i < amx.length; i++) { // Zeilen if(amx[i][i]) // bei ungerichtetem Graph, return false; // kein Pfeil auf sich selbst. for(int j = 0; j < i; j++) { // Spalten if(amx[i][j] != amx[j][i]) return false; } } return true; } Programm zum selber Testen: {{:pruefungen:bachelor:aud:graph.java.txt|:pruefungen:bachelor:aud:graph.java.txt}} **e)** public static boolean allReachable(boolean[][] ug, int node) { boolean[] visited = new boolean[ug.length]; visited[node] = true; visitAll(ug, visited, node); for(int i = 0; i < visited.length; i++) { if(!visited[i]) return false; } return true; } private static void visitAll(boolean[][] ug, boolean[] visited, int node) { for(int i = 0; i < ug.length; i++) { if(ug[node][i]) { if(!visited[i]) { visited[i] = true; visitAll(ug, visited, i); } } } } Programm zum selber Testen: {{:pruefungen:bachelor:aud:graph.java.txt|:pruefungen:bachelor:aud:graph.java.txt}} ==== Aufgabe 3 - Sortieren ==== **a)** 96 oder kleiner **b)** 2. Antwort ist richtig: Von Hinten nach Vorne **c)** ^ Stelle ^ Reihung ^^^^^^ | - | de | com | es | net | ch | ee | | 3 | de | es | ch | ee | com | net | | 2 | de | ee | net | ch | com | es | | 1 | ch | com | de | ee | es | net | **d)** public static char getChar(String str, int pos) { return (pos < str.length()) ? (str.charAt(pos)) : (char) 96; } public static void radixSort(String[] array) { ArrayList[] buckets = (ArrayList[]) new ArrayList[27]; for(int i = 0; i < 3; i++) { // Pro Stelle ein Durchgang for(int j = 0; j < 27; j++) { buckets[j] = new ArrayList(); } for(int j = 0; j < array.length; j++) { int bucketNumber = (int) getChar(array[j], 2 - i) - 96; buckets[bucketNumber].add(array[j]); } int k = 0; for(int j = 0; j < 27; j++) { for(String str : buckets[j]) { array[k++] = str; } } } } Programm zum selber Testen: {{:pruefungen:bachelor:aud:radixsort.java.txt|:pruefungen:bachelor:aud:radixsort.java.txt}} ==== Aufgabe 4 - Rekursion ==== **a)** Kaskadenartige Rekursion **b)** public static long[] pascalNiceRek(int n) { long[] result = new long[n+1]; long[][] dd = new long[n+1][n+1]; // ^ eigentlich Speicherplatzverschwendung // Besser: mit for-Schleife (mit i<=n): in dd[i] = new long[i+1]; for(int k = 0; k <= n; k++) { result[k] = pascalNiceRekH(dd, n, k); } return result; } private static long pascalNiceRekH(long[][] dd, int n, int k) { if(n < 0 || k < 0 || k > n) { return 0; } else if(n == 0 && k == 0) { return 1; } if(dd[n][k] == 0) { dd[n][k] = pascalNiceRekH(dd, n - 1, k - 1) + pascalNiceRekH(dd, n - 1, k); } return dd[n][k]; } **c)** public static long[] pascalNextLine(long[] current) { long[] result = new long[current.length + 1]; result[0] = 1; result[result.length - 1] = 1; for(int i = 1; i < result.length - 1; i++) { result[i] = current[i - 1] + current[i]; } return result; } Programm zum selber Testen: {{:pruefungen:bachelor:aud:pascal.java.txt|:pruefungen:bachelor:aud:pascal.java.txt}} ==== Aufgabe 5 - ADT ==== **a)** Ergebnis: 24.02.2011 \\ Regeln: P1 **b)** Ergebnis: 01.01.1970 \\ Regeln: F2, F2, F1, P2 **c)** setSeen(createPost(s, d, b), v) = createPost(s, d, v) **d)** getUrl(publish(P, id, F)) = getURL(F); **e)** revoke(id_1, publish(P, id_2, F)) = revoke(id_1, F) falls id_1 = id_2 = publish(P, id_2, revoke(id_1, F)) sonst Hinweis: getPost(id_1, revoke(id_1, F)) muss noPost liefern. \\ Das bedeutet, dass alle Posts mit ID id_1 gelöscht werden müssen. Hat man einen Post mit gesuchter ID gefunden, darf man nach dessen Löschen nicht aufhören, sondern muss "weitersuchen". **f)** markSeen(id_1, publish(P, id_2, F)) = publish(setSeen(P, true), id_2, F) falls id_1 = id_2 = publish(P, id_2, markSeen(id_1, F)) sonst ==== Aufgabe 6 - Graphen ==== **a)** [A] -> [B,9] -> [C,6] [B] -> [A,9] -> [E,3] -> [C,21] [F] -> [E,4] -> [K,2] -> [G,10] [Z] -> [H,5] -> [K,3] **b)** ^ A ^ B ^ C ^ D ^ E ^ F ^ G ^ H ^ K ^ Z ^ | [0] | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ | | 0 | 9 | [6] | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ | ∞ | | 0 | 9 | 6 | ∞ | ∞ | ∞ | [9] | ∞ | ∞ | ∞ | | 0 | [9] | 6 | 15 | ∞ | 19 | 9 | ∞ | ∞ | ∞ | | 0 | 9 | 6 | 15 | [12] | 19 | 9 | ∞ | ∞ | ∞ | | 0 | 9 | 6 | [15] | 12 | 16 | 9 | ∞ | ∞ | ∞ | | 0 | 9 | 6 | 15 | 12 | [16] | 9 | 17 | ∞ | ∞ | | 0 | 9 | 6 | 15 | 12 | 16 | 9 | [17] | 18 | ∞ | | 0 | 9 | 6 | 15 | 12 | 16 | 9 | 17 | [18] | 22 | | 0 | 9 | 6 | 15 | 12 | 16 | 9 | 17 | 18 | [21] | | 0 | 9 | 6 | 15 | 12 | 16 | 9 | 17 | 18 | 21 | **c)** Lösung: (D,H), (F,K), (B,E), (C,G), (K,Z), (D,E), (E,F), (A,C), (D,G) Wie viele Meter Kabel werden verlegt: **33** ==== Aufgabe 7 - wp-Kalkül==== Anmerkung: \\ Eine for-Schleife ist nicht direkt vom wp-Kalkül abgedeckt, lässt sich aber in eine äquivalente while-Schleife umformen: for(int i = 0; i < n; i++) { // Code } entspricht int i = 0; while(i < n) { // Code i++; } **a)** LO: Falsch Hier werden erst alle einzelnen Werte aufsummiert, dann der Betrag gebildet. RO: Richtig LU: Falsch Hier werden erst alle einzelnen Werte aufsummiert, dann der Betrag gebildet. RU: Falsch i < n darf nicht gelten, da sonst die Schleife nie terminieren würde **b)** wp("n = a.length; s = 0; i = 0;", s = ∑{from 0 to i - 1} |a_j| ∧ i ≤ n) = (0 = ∑{from 0 to 0 - 1} |a_j| ∧ 0 ≤ a.length) = (0 = 0 ∧ 0 ≤ a.length) = (true) **c)** Zu zeigen: {I ∧ b} => wp(A, I) wp(A, I) = wp("if (a[i] > 0) {s = s + a[i];} else {s = s - a[i]} i = i + 1;", s = ∑{from 0 to i - 1} |a_j| ∧ i ≤ n) = [(a[i] > 0) ∧ wp("s = s + a[i]; i = i + 1;", s = ∑{from 0 to i - 1} |a_j| ∧ i ≤ n)] ∨ [(a[i] ≤ 0) ∧ wp("s = s - a[i]; i = i + 1;", s = ∑{from 0 to i - 1} |a_j| ∧ i ≤ n)] = [(a[i] > 0) ∧ (s + a[i] = ∑{from 0 to i + 1 - 1} |a_j| ∧ i + 1 ≤ n)] ∨ [(a[i] ≤ 0) ∧ wp(s - a[i] = ∑{from 0 to i + 1 - 1} |a_j| ∧ i + 1 ≤ n)] = [(a[i] > 0) ∧ (s + a[i] = ∑{from 0 to i} |a_j|) ∧ (i + 1 ≤ n)] ∨ [(a[i] ≤ 0) ∧ (s - a[i] = ∑{from 0 to i} |a_j|) ∧ (i + 1 ≤ n)] = Überlegung: (a[i] > 0) ∧ (s + a[i] = ∑{from 0 to i} |a_j|) -> ist a[i] positiv, dann wird a[i] auf s addiert (a[i] ≤ 0) ∧ (s - a[i] = ∑{from 0 to i} |a_j|) -> ist a[i] negativ, dann wird -a[i] auf s addiert, also der Betrag von a[i] Man kann somit vereinfachen: = (s + |a[i]| = ∑{from 0 to i} |a_j|) ∧ (i + 1 ≤ n) {I ∧ b} => wp(A, I) {(s = ∑{from 0 to i - 1} |a_j| ∧ i ≤ n) ∧ (i < n)} => wp(A, I) = ¬(s = ∑{from 0 to i - 1} |a_j| ∧ i ≤ n) ∨ ¬(i < n) ∨ wp(A, I) = (s ≠ ∑{from 0 to i - 1} |a_j|) ∨ (i > n) ∨ (i ≥ n) ∨ wp(A, I) = (s ≠ ∑{from 0 to i - 1} |a_j|) ∨ (i ≥ n) ∨ wp(A, I) = (s ≠ ∑{from 0 to i - 1} |a_j|) ∨ (i ≥ n) ∨ [(s + |a[i]| = ∑{from 0 to i} |a_j|) ∧ (i ≤ n - 1)] = Überlegung: (i ≥ n) und (i ≤ n - 1) decken für den Integer den Zahlenstrahl ab, das heißt: Ist (i ≥ n) nicht erfüllt, dann ist (i ≤ n - 1) erfüllt und umgekehrt (s + |a[i]| = ∑{from 0 to i} |a_j|) = (s = ∑{from 0 to i} |a_j| - |a[i]|) = (s = ∑{from 0 to i - 1} |a_j|) und dies ist das Komplement zu (s ≠ ∑{from 0 to i - 1} |a_j|) Somit kann man schlussfolgern, dass die Invariante erfüllt ist! **d)** V = n - i i = 0; n = a.length; i++; in jedem Schleifendurchlauf ==== Aufgabe 8 - UML ==== class Firma { String name; Person geschaeftsfuehrer; Person[] mitarbeiter; Integer gibAnzahlMitarbeiter() { // Achtung: Objekt vom Typ Integer, nicht der primitive Datentyp int // ... } } class Person { String name; Integer gebJahr; Person vorgesetzter; Person[] unterstellteMitarbeiter; Person[] gibAlleUnterstelltenMitarbeiter() { // ... } }