Technische Universita t Mu nchen Institut fu r Informatik Lehrstuhl fu r Bioinformatik Einfu hrung in die Programmierung fu r Bioinformatiker Prof. B. Rost, L. Richter WS 2013/14 Aufgabenblatt 5 2. Dezember Methoden und Rekursion 5.1 (U ) Fragen zu Methoden (Wiederholung) (a) Was fu r Vorteile hat das Programmieren mit Methoden? (b) Was sind lokale Variablen in Methoden? Was sind Parameter? (c) Was ist mit dem U berladen von Methoden gemeint? (d) Was ist der Ru ckgabetyp einer Methode? Welche Rolle spielt hierbei das Schlu sselwort return? 5.2 (U ) Fragen zu Rekursion (a) Was versteht man unter Rekursion? (b) Was versteht man unter Endrekursion? (c) Kann man jedes iterative Problem in einen rekursiven Algorithmus u berfu hren? Wie sieht es mit der entgegengesetzten Richtung aus? (d) Was macht die folgende Funktion? void func ( int a ) { func ( a ); (e) Wie verhalten sich Parameter und lokale Variablen in Methoden bei erneuten Aufrufen derselben Methode? Verdeutlichen Sie dies am Beispiel der Fakulta tsfunktion: int fak ( int n ) { if ( n <= 1) { return 1; else { int tmp = n * fak ( n - 1); return tmp ; 5.3 (U ) Rekursion Betrachten Sie das folgende Programm: 1
public class Print { public static void main ( String [] args ) { print (3, 0); static void print ( int i, int depth ) { System. out. println (" start : depth = " + depth + ", i = " + i); if (i > 1) { print (i - 1, depth + 1); System. out. println (" end : depth = " + depth + ", i = " + i); (a) Überlegen Sie sich, welche Ausgabe der Aufruf print(3, 0) auf der Konsole erzeugt und notieren Sie sich diese auf einem Zettel. (b) Das abgedruckte Programm ist im Archiv zum Übungsblatt enthalten. Übersetzen Sie das Programm und führen Sie es anschließend aus, um die Programmausgabe mit der von Ihnen erwarteten Ausgabe zu vergleichen. (c) Überlegen Sie sich, wie sich die Ausgabe ändern würde, wenn im Anschluss an den rekursiven Aufruf print(i - 1, depth + 1) eine zusätzliches Statement print(i - 2, depth + 1) eingefügt würde. Notieren Sie sich wiederum die von Ihnen erwartete Ausgabe und passen Sie danach das Programm an, um Ihre Vermutung mit der tatsächlichen Ausgabe vergleichen zu können. 5.4 (Ü) Fibonacci-Folge Die Folge der Fibonacci-Zahlen ist definiert als: n 0 = 0 n 1 = 1 n i = n i 1 + n i 2 für i > 1 Schreiben Sie ein Programm Fibonacci, das nach Eingabe des Index i die Fibonaccizahl n i berechnet und ausgibt. Implementieren Sie dazu sowohl eine rekursive als auch eine iterative Lösung in den jeweiligen Methoden beziehungsweise public static long recursive(long i) public static long iterative(long i) Vergleichen Sie die beiden Lösungen hinsichtlich ihrer Laufzeit. Wie unterscheiden sich beide Lösungen hinsichtlich großer 1 i? 1 Wirklich gross! 2
5.5 (Ü) Türme von Hanoi Spielprinzip Das Spiel besteht aus drei Stäben A, B und C, auf die mehrere gelochte Scheiben gelegt werden, die alle verschieden groß sind. Zu Beginn liegen alle Scheiben auf Stab A der Größe nach geordnet mit der größten Scheibe unten und der kleinsten Scheibe oben. Ziel des Spieles ist es, den kompletten Scheiben-Stapel von A nach C zu versetzen. Bei jedem Zug darf die oberste Scheibe eines beliebigen Stabes auf einen der beiden anderen Stäbe gelegt werden, vorausgesetzt, dort liegt nicht schon eine kleinere Scheibe. Folglich sind zu jedem Zeitpunkt des Spieles die Scheiben auf jedem Feld der Größe nach geordnet. Aufgabe: Machen Sie sich mit dem Spielprinzip vertraut und versuchen Sie gemeinsam auf die Lösung des Spieles zu kommen. Erweitern Sie anschließend die Funktion move(...), so dass für eine beliebige Menge von Steinen die Einzelschritte (z.b. Move top stone from A to B. ) zur Lösung des Spiels auf der Konsole ausgegeben werden. 5.6 (H) Tagerechner (+++) Ziel dieser Aufgabe ist es, einen Tagerechner zu programmieren, mit dem man beispielsweise berechnen kann, welches Datum n Tage nach einem bestimmten Datum liegt. Nach dem Prinzip Divide and Conquer ( Teile und Herrsche ) sollen die Aufgaben in verschiedene Funktionen der Klasse DayCalculator zerlegt werden, bei der jede spätere Funktion (soweit möglich) die vorangegangenen nutzen soll. Implementieren Sie dazu folgende Funktionen und achten Sie darauf, dass Sie exakt die jeweils angegebene Funktions-Signatur 2 verwenden. (a) public static boolean isleapyear(int y) Prüft, ob es sich bei der Jahresangabe y um ein Schaltjahr handelt. Die Regel dabei lautet, dass alle durch 4 teilbaren Jahre Schaltjahre sind (z.b. 1996), außer wenn sie ebenfalls durch 100 teilbar sind (z.b. 1900). Durch 400 teilbare Jahre bilden davon wiederum eine Ausnahme und sind Schaltjahre (z.b. 2000). (b) public static int daysinyear(int y) und public static int daysinmonth(int m, int y) Geben an, wieviele Tage es in einem Jahr y gibt bzw. wieviele Tage es im Monat m eines bestimmten Jahres y gibt. (c) public static boolean isvaliddate(int d, int m, int y) Prüft, ob das angegebene Datum mit Tag d, Monat m und Jahr y existiert. (d) public static String getdateasstring(int d, int m, int y) Gibt das Datum im Format 1.11.2009 als String zurück. Bei einem unzulässigen Datum soll die Zeichenkette Invalid Date zurückgegeben werden. (e) public static int remainingdaysinmonth(int d, int m, int y) Gibt an, wieviele Tage abzüglich des gegebenen Datums im Monat verbleiben. Bei einem 2 d.h. korrekte Verwendung von Schlüsselworten, richtiger Rückgabetyp, korrekter Funktionsname (inkl. Groß- und Kleinschreibung) sowie richtige Anzahl, Reihenfolge und Typen der Funktionsparameter 3
unzulässigen Datum soll der Wert 0 zurückgegeben werden. (f) public static int remainingdaysinyear(int d, int m, int y) Gibt an, wieviele Tage abzüglich des gegebenen Datums im Jahr verbleiben. Bei einem unzulässigen Datum soll der Wert 0 zurückgegeben werden. (g) public static String daysadd(int d, int m, int y, int numdays) Gibt an, welches Datum numdays Tage hinter dem angegebenen Datum liegt. Das Ergebnis soll auf der Konsole ausgegeben werden. Falls das initiale Datum unzulässig ist, soll die Zeichenkette Invalid Date zurückgegeben werden. (h) Sofern noch nicht geschehen, soll die Funktion daysadd so erweitert werden, dass auch negative Zahlen zulässig sind. (i) Kommentieren Sie alle Funktionen inklusive aller Parameter und Rückgabewerte entsprechend der JavaDoc-Richtlinien. Anmerkungen: Sie dürfen für diese Aufgabe natürlich nicht auf die Klasse Calendar aus der Standardbibliothek zurückgreifen. Für die Teilaufgaben (g) ist es sinnvoll, sich vorab einen Algorithmus zu überlegen, wie man für beliebige Parameter möglichst einfach und schnell zum Ergebnis kommt. Versuchen Sie dabei, nach dem Prinzip Teile und Herrsche das allgemeine Problem in Teilaufgaben zu zerlegen, die einfach zu implementieren sind. Versuchen Sie bei der Teilaufgabe (h), einen möglichst einfachen Weg für die Lösung des Problems zu finden. 5.7 (H) Ackermann-Funktion (++) Die Ackermann-Funktion ist definiert als: n + 1 für m = 0 m, n N 0 : ack(m, n) := ack(m 1, 1) für n = 0 ack(m 1, ack(m, n 1)) sonst Sie liefert sehr schnell sehr große Ergebnisse und Verschachtelungstiefen. Schreiben Sie ein Programm Ackermann, das Werte für m und n mittels Tools.readInt() einliest und eine kleine Tabelle für alle Ergebnisse zwischen 0 und der Benutzereingabe ausgibt. Kann ein Tabelleneintrag nicht mit maximal 10.000.000 Funktionsaufrufen berechnet werden, soll an dieser Stelle ein x ausgegeben werden. Implementieren Sie hierfür die Funktion public static int recursiveack(int m, int n) Beispiel für m=4 und n=4: m\n 0 1 2 3 4 0: 1 2 3 4 5 1: 2 3 4 5 6 2: 3 5 7 9 11 4
3: 5 13 29 61 125 4: 13 x x x x 5