Technische Informatik für Ingenieure – WS 2010/2011 Musterlösung Übungsblatt Nr. 5 8. November 2010 Übungsgruppenleiter: Matthias Fischer Mouns Almarrani Rafał Dorociak Michael Feldmann Thomas Gewering Benjamin Koch Dominik Lüke Alexander Teetz Simon Titz Simon Oberthür Seite 1(4) Aufgabe 1: (Codevereinfachungen) Vereinfachen Sie folgende Codestücke. Verwenden Sie Zusicherungen, um sich zu überlegen was zu Beginn der einzelnen Blöcke gilt. a. if (a == 0) a = 2 * c; else if (c != 0) a = a * b + 2 * c; else a= a * b; b. if (x < a else if a else if a else a 0 && y < 0) = x * y; (x < 0) = x * (-y); (y > 0) = (-x) * (-y); = x * (-y); Lösung: a. if (a == 0) a = 2 * c; else if (c != 0) a = a * b + 2 * c; else a= a * b; // a == 0 // a != 0 und c != 0 // a != 0 und c == 0 Erste Zusicherung: klar Zweite Zusicherung: Die Bedingung aus der ersten Zusicherung darf nicht erfüllt sein, da sie im else-Block der ersten Bedingung liegt. Also gilt hier: a != 0. Dritte Zusicherung: Hier darf weder die Bedingung aus der ersten noch die aus der zweiten Zusicherung gelten. D.h. es darf weder a == 0 noch (a != 0 und c != 0) gelten. Also gilt: a != 0 und nach De Morgan: a == 0 oder c == 0. Da beides gelten muss, folgt: a != 0 und c == 0. Technische Informatik für Ingenieure Seite 2(4) Zur Vereinfachung: Der Vergleich zwischen der ersten und zweiten Zuweisung zeigt, dass im zweiten Fall a * b zusätzlich addiert wird. Wenn aber a gleich 0 ist (wie in der ersten Zusicherung), dann haben beide Zuweisungen denselben Effekt. Dasselbe gilt für den Vergleich zwischen der zweiten und dritten Zuweisung mit dem Ausdruck 2 * c. Somit kann das Codestück zusammengefasst werden zu: a = a * b + 2 * c; (Beachten Sie, dass dieses Codestück zwei Multiplikationen und eine Addition enthält, die immer ausgeführt werden. Beim ursprünglichen Code gibt es zwar drei Vergleichsoperationen mehr, aber manchmal wird eine Multiplikation und eine Addition gespart.) b. if (x < a else if a else if a else a 0 && y < 0) = x * y; (x < 0) = x * (-y); (y > 0) = (-x) * (-y); // x < 0 und y < 0 // x < 0 und y >= 0 // x >= 0 und y > 0 // y <= 0 und y <= 0 = x * (-y); Erste Zusicherung: klar Zweite Zusicherung: Da wir uns hier im else-Block der ersten Zusicherung befinden, darf hier nicht gelten: x < 0 und y < 0. Nach De Morgan gilt also: x >= 0 oder y >= 0. Zusammen mit der Bedingung in der zweiten Zusicherung ergibt sich: x < 0 und y >= 0. Dritte und vierte Zusicherung: Analog zur zweiten Zusicherung. Zur Vereinfachung: Anhand der Zusicherungen sehen wir, dass immer dann, wenn eine ungerade Anzahl der Variablen x und y negativ ist, eine ungerade Anzahl von negativen Vorzeichen in den darauffolgenden Zuweisungen stehen. Es wird der Variablen a also immer eine positive Zahl mit dem Betrag von x * y zugewiesen. Das lässt sich wie folgt kürzer schreiben: a = x * y; if (a < 0) a = -a; Aufgabe 2: (Schleifen am Beispiel Primfaktorzerlegung) Schreiben Sie ein Programm, das eine positive ganze Zahl n einliest und in ihre Primfaktoren zerlegt. Unter der Primfaktorzerlegung versteht man die Darstellung einer natürlichen Zahl als Produkt von Primzahlen. Die Zahl 30 besteht zum Beispiel aus den Primfaktoren 2, 3, 5; die Zahl 100 aus den Primfaktoren 2, 2, 5, 5. Hinweise: Eine Primzahl p ist eine natürliche Zahl mit genau zwei natürlichen Zahlen als Teiler, nämlich 1 und p, z. B. sind 2, 3, 5, 7, 11,...Primzahlen. Jede natürliche Zahl besitzt eine (bis auf die Reihenfolge der Faktoren) eindeutige Primfaktorzerlegung (Fundamentalsatz der Arithmetik). • Ihr Programm könnte bei Eingabe einer positiven ganzen Zahl n wie folgt arbeiten: Versuche die Zahl n in aufsteigender Reihenfolge durch die Zahlen 2, 3, 4, 5,... zu teilen. Wenn n durch eine Zahl p teilbar ist, Technische Informatik für Ingenieure Seite 3(4) dann wird n um den Faktor p verkleinert (d.h. es wird n = n/p gesetzt) und p ausgegeben. Teile n nun solange durch p und verkleinere n dabei jeweils um den Faktor p, bis n nicht mehr durch p teilbar ist. Anschließend erhöhe p um 1 und fahre wie oben fort. • Überlegen Sie sich zusätzlich, warum in diesem Programm auch tatsächlich nur Primzahlen ausgegeben werden. Lösung: public class Primfaktorzerlegung { public static void main(String[] args) { Out.print("Geben Sie eine natuerliche Zahl ein: "); int n = In.readInt(); int t = 2; while (n > 1) { if (n % t == 0) { Out.print(t + " "); n /= t; } else t++; } } } Das oben angegebene Programm gibt auch tatsächlich nur Primzahlen aus, da: Angenommen, das Programm würde eine Zahl t ausgegeben, die keine Primzahl ist. Dann existiert eine natürliche Zahl k mit 1 < k < t, die t teilt. Da t die Zahl n teilt (denn t wur de ausgegeben), teilt auch k die Zahl n. In den vorherigen Schleifeniterationen, in denen n auf Teilbarkeit durch k getestet wurde, wurde n so lange um den Faktor k verringert, bis k kein Teiler von n mehr ist. Da n in den darauf folgenden Iterationen nur noch um Faktoren k’ > k, die Teiler von n sind, verringert wird, kann k auch später kein Teiler von n mehr sein, womit wir einen Widerspruch erhalten. Aufgabe 3: ( Schleifen am Beispiel kleinste gemeinsame Vielfache) Schreiben Sie ein Programm, das für zwei eingegebene positive Zahlen m und n das kleinste gemeinsame Vielfache (kgV) bestimmt. Hinweis: Das kgV zweier positiver Zahlen m und n ist die kleinste positive ganze Zahl, die sowohl durch m als auch durch n teilbar ist, zum Beispiel: das kgV von 4 und 6 ist 12. Um den kgV zweier positiver Zahlen zu berechnen, könnten sie beispielsweise die natürlichen Zahlen beginnend bei der größten der beiden Zahlen daraufhin testen, ob sie Vielfache beider Zahlen sind. Bei der ersten solchen Zahl wird diese ausgegeben und das Programm beendet. Lösung: public class kgV { public static void main(String[] args) { Out.print("Geben sie die erste positive Zahl ein: "); int m = In.readInt(); Out.print("Geben sie die zweite positive Zahl ein: "); int n = In.readInt(); int i, ergebnis; Technische Informatik für Ingenieure Seite 4(4) if (m < n) i = n; else i = m; if ((m <= 0) || (n <= 0)) { Out.print("(Mindestens) eine der beiden eingegebenen Zahlen ist nicht positiv."); } else { while ((i % m != 0) || (i % n != 0)) i++; ergebnis = i; Out.println("kgV von " + m + " und " + n + ": " + ergebnis); } } } Aufgabe 4: (Schleifeninvarianten) Beweisen Sie die für das folgende Programm vorgeschlagene Schleifeninvariante und schließen Sie daraus, dass das Programm bei Eingabe einer Zahl n die Zahl n² berechnet. int n=In.readInt(); int sum=0; for(int i=1; i<=n; i++) { //Invariante: sum=(i-1)^2 sum+=2*i-1; } Out.println(“Das Ergebnis ist: “+sum); Lösung: • • • Initialisierung: Die Schleifeninvariante gilt vor der ersten Ausführung der Schleife mit Index i=1, da sum mit 0 initialisiert wird und (i-1)² für i = 1 ebenfalls 0 ergibt. Erhaltung: Die Schleifeninvariante gelte vor der Ausführung der Schleife mit Index i. D.h. es gelte: sum = (i-1)². Wir müssen zeigen, dass die Schleifeninvariante auch noch nach der Ausführung mit Index i, also vor der Ausführung mit Index i’ = i+1 gilt. D.h. wir müssen zeigen, dass vor der Ausführung der Schleife mit Index i’ gilt: sum = (i’-1)². In dem Schleifendurchlauf mit Index i wird sum’ = sum + 2*i-1 gesetzt. Nach Schleifeninvariante gilt: sum = (i-1)². Nach der Zuweisung gilt somit: sum’ = (i-1)² + 2*i-1 = i² - 2*i + 1 + 2*i - 1 = i² = (i’-1)². Terminierung: Nach der letzten Ausführung der Schleife gilt: i = n+1. Somit gilt, dass das Programm (i-1)² = n² berechnet.