Übungsblatt Nr. 3

Werbung
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.
Herunterladen