Übung zur Vorlesung Einführung in die Programmierung“, WS 14/15 ” Übungsleiter: Sebastian Ebers <[email protected]> Aufgabenblatt 4 https://moodle.uni-luebeck.de/mod/assign/view.php?id=11542 Abgabe: 15.12.2014, vor der Vorlesung / Großübung (10:00 Uhr, AM 1) Max. Punktzahl: 76 Theoretischer Teil (für die Bearbeitung zu Hause) Aufgabe 4.1: Iteration und Rekursion (7 Punkte) a) Gegeben sei folgendes Programm: 1 2 3 4 5 6 7 8 fun sum(arr[1..n] : int) : int {{ var s:=0; for i:=1 to n do s := s+arr[i]; od; return s; }} Formen Sie den gegebenen iterativen Algorithmus in eine rekursive Funktion in Pseudocode um. Die Methodensignatur darf hierbei verändert werden. (3 P.) 1 b) Gegeben sei folgendes Programm: 1 2 3 4 5 6 7 8 9 10 fun c(arr[1..n] : int, elem : int, index : int) : bool {{ if index < 1 or index > n then return false; else if arr[index] = elem then return true; else return c(arr, elem, index+1); fi; }} i) Was berechnet das Programm? (1 P.) ii) Formen Sie den gegebenen rekursiven Algorithmus in eine iterative Funktion in Pseudocode um. (3 P.) Aufgabe 4.2: Schleifen (9 Punkte) Die Schleifenarten for, while und do-while besitzen die selbe Ausdrucksstärke. So lassen sich Programme, die eine Schleifenart verwenden, in ein semantisch äquivalentes Programm umschreiben, welches eine andere Schleifenart verwendet. Anstatt der in der Vorlesung vorgestellten Notation für die for-Schleife (for i:= 1 to n do α od;), soll im Folgenden die (aus C++ und Java bekannte) Notation gelten: for ( α; β ; γ ; ) { δ ; }, wobei α die Initialisierung der Schleife ist, β die Schleifenbedingung, γ die Modifikation und δ eine oder mehrere Anweisungen. Formen Sie die folgenden Schleifenarten um in ein semantisch äquivalentes Programm um. Nutzen Sie dabei immer alle Anweisungen α, β, γ, δ. a) Formen Sie folgendes Programm in ein Programm mit while-Schleife um: for ( α; β ; γ ; ) { δ ; } (3 P.) b) Formen Sie folgendes Programm in ein Programm mit while-Schleife um: α ; do { δ ; γ ;} while(β) (3 P.) c) Formen Sie folgendes Programm in ein Programm mit do-while-Schleife um: for ( α; β ; γ ; ) { δ ; } (3 P.) 2 Aufgabe 4.3: Imperative Algorithmen (7 Punkte) Gegeben sei folgendes Programm: 1 2 3 4 5 6 7 PRG: var x,y,t : int; intput x,y; t := y; y := x; x := t; output x,y; a) Was berechnet das Programm? (1 P.) b) Welche Signatur besitzt PRG ? (2 P.) c) Werten Sie PRG mit Hilfe der in der Vorlesung vorgestellten Semantikfunktion [[]] für die Eingabe x = 12 und y = 24 aus. (4 P.) 3 Java-Kurs (für die betreute Rechnerzeit) Aufgabe 4.4: Methodenaufrufe (18 Punkte) Gegeben sei folgendes Programm: 1 public class Methodenaufrufe { 2 3 4 5 6 7 8 public static void summiere(byte a, int b) { System.out.println("Methode ’summiere’:"); System.out.println("a: " + a); System.out.println("b: " + b); System.out.println("Ergebnis: " + (a + b)); } 9 10 11 12 13 14 15 public static void addiere(int summandA, byte summandB) { System.out.println("Methode ’addiere’:"); System.out.println("SummandA: " + summandA); System.out.println("SummandB: " + summandB); System.out.println("Ergebnis: " + (summandA + summandB)); } 16 17 18 19 20 21 22 public static void subtrahiere(int a, byte b) { System.out.println("Methode ’subtrahiere’:"); System.out.println("’a’: " + a); System.out.println("’b’: " + b); System.out.println("Ergebnis: " + (a - b)); } 23 24 25 26 27 28 29 30 public static void dividiere(double dividend, double divisor) { System.out.println("Methode ’dividiere’:"); System.out.println("’Dividend’: " + dividend); System.out.println("’Divisor’: " + divisor); System.out.println("Ergebnis: " + (dividend / divisor)); } 31 32 33 34 35 36 37 public static void multipliziere(double mult1, int mult2) { System.out.println("Methode ’multipliziere’:"); System.out.println("’Multiplikator’: " + mult2); System.out.println("’Multiplikand’: " + mult1); System.out.println("Ergebnis: " + (mult2 * mult2)); } 38 39 40 41 42 43 public static void main(final String[] args) { byte a = 3; int b = 5; summiere(a, b); addiere(b, a); 44 45 46 47 byte subtrahend = 2; int minuend = 4; subtrahiere(minuend, subtrahend); 48 49 50 51 double dividend = 7; double divisor = 14.0; dividiere(divisor, dividend); 52 53 multipliziere(7.5, 4); 54 55 56 57 58 59 System.out.println("Methode ’main’:"); System.out.println(a + "/" + b + " = " + (b / a)); } } 4 a) Geben Sie an, was auf der Konsole ausgegeben wird und welchen Typ die verwendeten Variablen haben. b) Nennen Sie die Besonderheiten oder Probleme, die sich beim Aufruf der Methode dividiere ergeben. Liefert sie das vom Aufrufer erwartete Ergebnis? (1 P.) c) Warum kann das Problem, das sich bei dividiere ergibt, bei subtrahiere nicht auftreten? (1 P.) Aufgabe 4.5: Funktionen (14 Punkte) a) Eine mögliche Lösung für die Aufgabe Schleifen vom letzten Übungsblatt könnte wie folgt aussehen: 1 2 3 // Schleifen.java public class Schleifen { public static void main(String[] args) { 4 int n = Integer.parseInt(args[0]); long erg = 1; for (int i = 1; i <= n; i++) { erg = erg * i; } System.out.println("Fakultaet von "+args[0]+" ist "+erg); 5 6 7 8 9 10 11 int x = Integer.parseInt(args[1]); int y = Integer.parseInt(args[2]); int a = x; int b = y; while (x != y) { if (x > y) { x = x - y; } else { y = y - x; } } System.out.println("GGT von "+a+" und "+b+" ist "+x); 12 13 14 15 16 17 18 19 20 21 22 23 24 25 } } Da die Main-Methode mit den Berechnungen der beiden Algorithmen etwas unübersichtlich geworden ist, lagern wir die Berechnungen der Algorithmen in zwei Methoden aus, in denen die Berechnung durchgeführt und das Ergebnis zurückgegeben wird. Erzeugen Sie zunächst eine neues Programm mit dem Namen Funktionen.java und speichern Sie die ersten drei Übergabeparameter ähnlich wie oben. (1 P.) 5 b) Lagern Sie die Berechnung der Fakultät in eine eigene Methode mit folgender Signatur aus: public static long fakultaet(int n) Die Methode berechnet die Fakultät des Übergabeparameters n und gibt das Ergebnis zurück an die aufrufende Methode. (2 P.) c) Lagern Sie die Berechnung des größten gemeinsamen Teilers in eine eigene Methode mit folgender Signatur aus: public static int ggT(int x, int y) Hier wird der größte gemeinsame Teiler von x und y berechnet und an die aufrufende Methode zurückgeliefert. (2 P.) d) Rufen Sie aus der Main-Methode heraus die beiden Funktionen fakultaet(int n) und ggT(int x, int y) auf, berechnen Sie für den ersten Übergabeparameter die Fakultät, danach den GGT von Parameter zwei und drei. Geben Sie jeweils die Rückgaben der Methoden aus. (3 P.) e) Angenommen, Sie wollen eine Zahlenkombination für einen Tresor auswählen. Es stehen 20 Zahlen zur Verfügung und Sie benötigen drei verschiedene Zahlen für die Kombination. Dann gibt es P (20, 3) Möglichkeiten für diese Zahlenkombination, wobei P definiert wird durch die Formel P (a, b) = (a!)/(a − b)! Benutzen Sie die von Ihnen implementierte Methode fakultaet(int n), um die beschriebene Formel für die Werte a = 20 und b = 3 in der Main-Methode zu berechnen. (3 P.) f) Lagern Sie nun auch diese Formel in eine Methode mit dem Namen tresorKombinationen aus. Die Methode erhält die beiden Werte a und b als Übergabewerte und liefert als Ergebnis die Anzahl der möglichen Kombinationen zurück. Überlegen Sie sich hierzu zunächst, wie die Übergabeparameter und der Rückgabewert aussehen müssen, damit der Zahlenraum zur Übergabe der Werte ausreicht und begründen Sie Ihre Wahl kurz. Rufen Sie die Funktion tresorKombinationen aus der Main-Methode mit den Werten a = 20 und b = 3 auf. (3 P.) 6 Aufgabe 4.6: Iteration und Rekursion (11 Punkte) Die folgenden Teilaufgaben sollen in die rekursive Programmierung einführen und den Unterschied zwischen iterativer und rekursiver Programmierung verdeutlichen. Erstellen Sie dazu ein Programm Rekursion.java. Als Lösung ist der gesamte kommentierte Code abzugeben. Es ist nicht nötig, einzelne Abgaben zu den jeweiligen Aufgabenteilen anzufertigen. a) Fakultät In der Aufgabe Funktionen haben Sie eine iterative Berechnung der Fakultät implementiert: n Y n! = 1 · 2 · 3 · ... · n = k k=1 Diese Art der Programmierung durch Wiederholung der gleichen Anweisung (beispielsweise in einer FOR-Schleife) wird als iterativ (lat. iterare, wiederholen) bezeichnet. Eine andere Möglichkeit ist die rekursive (lat. recurrere, zurücklaufen) Lösung. Dabei wird innerhalb der Methode die Methode selbst aufgerufen. (i) Implementieren Sie die Methode fakultaet rekursiv. Verwenden Sie dazu die rekursive Definition der Fakultät: 1 falls n = 0 n! = n · (n − 1)! falls n > 0 Hinweis: Fakultäten für negative Zahlen sind nicht definiert. (2 P.) (ii) Schreiben Sie ein Hauptprogramm (es genügt auch eine Main-Methode innerhalb der im vorherigen Teil erstellten Klasse, das die Ergebnisse der Rekursion von n = 1 bis n = 12 ausgibt. (1 P.) 7 b) Kaninchen Auf einer Insel setzt ein geschäftstüchtiger Kaninchenzüchter ein junges Kaninchenpaar aus. Die Tiere sind nach 2 Monaten geschlechtsreif und bekommen dann jeden Monat ein Paar als Nachwuchs. Im dritten Monat gibt es also ein neues Paar, welches im 5. Monat selbst wieder Nachwuchs bekommt usw.. Dabei sei vorausgesetzt, dass immer genug Futter vorhanden ist, der Nachwuchs immer ein Männchen-Weibchen-Paar ist, keine Fressfeinde auf der Insel leben und die Kaninchen unsterblich sind. (i) Schreiben Sie ein rekursives Programm, welches die Anzahl der Kaninchenpaare im Monat n ausgibt. Die Populationsstärke wird durch die Fibonacci-Folge f1 , f2 , ... mit der rekursiven Bildungsvorschrift fn = 0 falls n = 0 1 falls n = 1 fn−1 + fn−2 falls n ≥ 2 definiert. Erstellen Sie dazu die Methode fibonacci, welche die Anzahl der Paare im n-ten Monat ausgibt. Erweitern Sie Ihr Hauptprogramm so, dass die Anzahl der Paare der Monate 1 bis 12 und außerdem die Anzahl der Paare nach 2, 3, 4 und 5 Jahren am Bildschirm angezeigt werden. (3 P.) (ii) Was fällt Ihnen bei der (Dauer der) Berechnung der letzten beiden Werte auf? (1 P.) (iii) Warum tritt dieser Effekt auf? (2 P.) (iv) Implementieren Sie die Rekursion in einer neuen Methode so, dass dieser Effekt nicht auftritt! (2 P.) Hinweis: Die Lösung der Teilaufgabe (iv) ist deutlich schwerer, als die Anzahl der Punkte vermuten lässt. 8 Aufgabe 4.7: Gültigkeitsbereiche (10 Punkte) Analysieren Sie das folgende Programm und achten Sie dabei insbesondere auf die verschiedenen Gültigkeitsbereiche der Klasse, der Methoden und der lokalen Variablen. Was wird auf der Konsole ausgegeben, wenn das Programm mit dem Übergabeparameter 23 aufgerufen wird? Begründen Sie Ihre jeweilige Antwort kurz. (10 P.) 1 2 // Gueltigkeitsbereiche.java public class Gueltigkeitsbereiche { 3 4 static int i = 10; 5 6 public static void main(String[] args) { 7 System.out.println("a) i = "+i); 8 9 i = Integer.valueOf(args[0]); 10 11 System.out.println("b) i = "+i); 12 13 int i = 6; 14 15 System.out.println("c) i = "+i); 16 17 classMethod(); 18 19 System.out.println("f) i = "+i); 20 21 Gueltigkeitsbereiche bereiche = new Gueltigkeitsbereiche(); System.out.println("j) j = " + bereiche.memberMethod()); 22 23 24 } 25 26 public static void classMethod(){ 27 System.out.println("d) i = "+i); 28 29 int i = 42; 30 31 System.out.println("e) i = "+i); 32 33 } 34 35 public int memberMethod(){ 36 int j = 88; { j = 5; System.out.println("g) i = "+(++i)); System.out.println("h) j = "+j); } 37 38 39 40 41 42 43 System.out.println("i) i = " + i); return j; 44 45 46 47 } } 9