Algorithmen und Programmierung 2, SS 2016 — 1. Übungsblatt Aufgaben zum Vorkurs, ohne Abgabe und ohne Bewertung. Teil A, für Studierende ohne Programmiererfahrung 1. Kalenderrechnungen (a) Schreiben Sie eine Funktion, die für ein Datum (Jahr, Monat, Tag) im gregorianischen Kalender die Anzahl der Tage berechnet, die seit dem 1901–01–01 vergangen sind. Für 1900–12–31 sollte das Ergebnis also −1 sein. (b) Schreiben Sie eine Funktion zur Bestimmung des Wochentags für ein gegebenes Datum. (c) Schreiben Sie eine Funktion, die berechnet, wie oft der 13. seit dem 1700–01–01 bis heute auf einen Samstag, Sontag, Montag, Dienstag, . . . oder Freitag gefallen ist. (d) Die Länge des tropischen Jahres (also des mittleren astronomischen Sonnenjahres, das die Jahreszeiten bestimmt) beträgt etwa 365,242 19 Tage. Der gregorianische Kalender hat eine Periode von 400 Jahren. Welche mittlere Länge hat das gregorianische Kalenderjahr? Wann wird sich die Abweichung zwischen dem gregorianischen Kalender und der tropischen Jahreslänge so weit aufsummiert haben, dass man den Kalender um einen ganzen Tag korrigieren müsste, um die Abweichung auszugleichen? Muss man dann einen Extraschalttag einschieben, oder muss man einen Schalttag ausfallen lassen? 2. Schleifen, Textbilder Schreiben Sie ein Programm, das folgende Pyramide zunächst als Liste von Zeilen erzeugt und dann ausgibt. 1 121 12321 1234321 123454321 12345654321 1234567654321 3. Summen Berechnen Sie mit Gleitkommaarithmetik die Summen 100 10000 X 1 X 1 , , n n n=1 n=1 1000000 X n=1 100 10000 1 X 1 X 1 , , , n n2 n2 n=1 n=1 1000000 X n=1 1000 20 X 1 1 X 1 , , und . 2 n n! n! n=1 n=1 4. Priorität einstelliger und zweistelliger Operatoren Was ist das Ergebnis der folgenden Rechnungen? a=10; -a//3, 0-a//3, a//-3, 0-a//3, -a%3, 0-a%3, a%-3, 0-a%3 Versuchen Sie, die Antwort vorherzusagen, und probieren Sie es dann aus. Gilt in jedem Fall die Formel (a//b)*b + a%b = a? 1 5. Funktionen Schreiben Sie Funktionen, die als Argument eine Liste von Zeilen erhalten und das dargestellte Bild (wie in Aufgabe 2) (a) vertikal spiegeln (unten und oben vertauschen), (b) horizontal spiegeln (links und rechts vertauschen), (c) um 90◦ nach links drehen. Die einzelnen Zeilen sollen kein Zeilenendezeichen "\n" enthalten, und sie dürfen verschieden lang sein. 6. Grafik, Vektorrechnung Das nebenstehende Bild zeigt einen Ausschnitt aus dem Bild Fibonacci meets Pythagoras“ von Eugen ” Jost1 , das aus Quadraten besteht. (Insbesondere die kleineren Quadrate sind jedoch nicht sehr genau gezeichnet.) Gleich große Quadrate haben dieselbe Farbe. Schreiben Sie eine Funktion, die mit dem turtlePaket die ersten n Quadrate dieser Spirale zeichnet. (Bei Jost sind es n = 25 Quadrate.) Zusatzaufgabe: Das Bild soll eine vorgegebene Rechtecksfläche mit den Ausmaßen L × B möglichst gut ausfüllen und in dieser Fläche zentriert sein. 7. Schleifenabbruch Das folgende Programm soll alle Dateien im aktuellen Dateiordner auflisten, deren Name mit .txt aufhört, und in denen vor dem Vorkommen des ersten Symbols !“ ” jede Zeile das Symbol +“ enthält. ” import os for Name in os.listdir(): if Name.endswith(".txt"): Datei = open(Name, encoding=’utf-8’, errors=’ignore’) for Zeile in Datei: for Zeichen in Zeile: if Zeichen == "+": break if Zeichen == "!": continue else: continue else: print (Name) Datei.close() Welche Dateien werden von diesem Programm tatsächlich ausgegeben? Stellen Sie das Programm richtig. 1 Aus dem Mathematikkalender Alles ist Zahl“, April 2010, siehe http://mathematik-und-kunst.de/ ” 2 8. Unveränderliche und veränderliche Datenstrukturen Was wird von der folgenden Befehlsfolge ausgegeben? Welche Befehle sind ungültig? a=[1,3,(4,2)] b=(a,a,[1,5,4,3]) print(b[1][2]) b[1][2]="wer" print(b) a[2][1] a[2][1]="wann" print(b) b[2][1] b[2][1]=["wo","wie"] print(b) b[2]=[2,4] print(b) a="warum" print(b) Teil B, für Studierende mit Programmiererfahrung 9. Sitzplatzzuteilung bei einer Klausur. Bei einer Klausur sollen n Prüflinge in einen oder mehrere Hörsäle gepfercht werden, sodass möglichst viele Teilnehmerinnen einen möglichst großen Mindestabstand voneinander ha” ben.“ Im Netz finden Sie, wie eine Lösung als PDF-Datei2 oder als Textgraphik3 ausschauen könnte. Erstellen Sie eine kleine Anwendung in Python, die diese Aufgabe unterstützt. Dabei müssen Sie einige Aspekte der Aufgabe selbst noch genauer spezifizieren. Hier sind verschiedene Teilaufgaben, die Sie auch unabhängig voneinander oder gemeinsam mit anderen Teams in Angriff nehmen können. (a) Überlegen Sie sich ein geeignetes Eingabeformat für Hörsäle. (b) Schreiben Sie ein Programm zur Ausgabe einer Lösung (i) grafisch, zur Übersicht4 , und (ii) in Listenform, damit die Prüflinge ihre Plätze finden können. (c) Formulieren Sie Kriterien, nach denen man verschiedene vorgeschlagene Lösungen vergleichen kann. Zum Beispiel sollte es in der ersten Lösung2 besser bewertet werden, wenn die freien Plätze links und rechts von #19 und #28 gleichmäßiger aufgeteilt werden. (d) Schreiben Sie ein Programm, das zwei vorgeschlagene Lösungen anhand dieser Kriterien miteinander vergleicht, zum Beispiel, indem es für jede Lösung eine Maßzahl für die Güte berechnet. (e) Versuchen Sie, einen möglichst guten Sitzplan zu erstellen. 2 http://www.inf.fu-berlin.de/lehre/WS15/ALP3/Hoersaal2.pdf http://www.inf.fu-berlin.de/lehre/SS16/ALP2/hoersaalraster.txt 4 Eine einfache Möglichkeit zur Erstellung einer graphischen Ausgabe bietet das Python-Paket turtle. Das erstellte Bild kann dann zum Beispiel als Bildschirmfoto aufgenommen werden oder mit dem Befehl Screen().getcanvas().postscript(file=’Ausgabe.eps’) in eine PostScript-Datei umgewandelt werden. 3 3 Algorithmen und Programmierung 2, SS 2016 — 2. Übungsblatt Abgabe bis Freitag, 29. April 2016, 12:00 Uhr, Hinweise zu den Programmieraufgaben in Python: Geben Sie Ihrem Programm für Aufgabe N den Namen AufgabeN _Nachname1 _Nachname2 .py. Beim Auruf dieses Programms aus der Kommandozeile soll eine Testsuite ablaufen, in der jede Funktion mindestens einmal aufgerufen wird. Sie dürfen Unterprogramme in getrennte Module packen, aber die Modulnamen müssen in der gleichen Weise mit Ihren Nachnamen gekennzeichnet sein. Laden Sie die Programme elektronisch im KVV hoch, und geben Sie zusätzlich die ausgedruckten Programme zusammen mit den anderen Lösungsbestandteilen in Papierform in die Fächer der Tutoren ab. 1) Verwenden Sie sprechende Namen für Variablen und Funktionen, die den Inhalt der Variablen oder die Funktionalität der Funktion charakterisieren. 2) Verwenden Sie die vorgegebenen Funktionsnamen, falls diese angegeben sind. 3) Kommentieren Sie das Programm. 4) Verwenden Sie geeignete Hilfsvariablen und Hilfsfunktionen. 5) Löschen Sie Programmzeilen und Variablen, die nicht verwendet werden. 10. Wörterbücher und Mengen, Sammelbilder, 0 Punkte (a) Untersuchen Sie experimentell, wie oft man eine zufällige Zahl zwischen 1 und n wählen muss, bis jede Zahl vorgekommen ist. Machen Sie für n = 10, 100, und 1000 jeweils 100 Experimente, und bestimmen Sie für jedes n das Minimum, das Maximum, und den Mittelwert der Anzahl der Zahlen. Ein Experiment besteht dabei aus der notwendigen Anzahl von Versuchen, bis man alle Zahlen beobachtet hat. (Zufällige ganze Zahlen kann man mit der Bibliotheksfunktion random.randint erzeugen. Der Erwartungswert für die Anzahl der Versuche ist übrigens n(1 + 21 + 13 + · · · + n1 ).) (b) Bestimmen Sie bei jedem Experiment auch, wie oft die häufigste Zahl vorgekommen ist, und machen Sie eine analoge Statistik wie bei Aufgabe (a). 11. Boolesche Ausdrücke, 0 Punkte Schreiben Sie die Funktion schaltjahr1 zum Testen, ob ein Jahr ein Schaltjahr ist, möglichst knapp mit einem einzigen Booleschen Ausdruck und ohne if-Anweisungen. (Ist diese Fassung leichter zu verstehen als das ursprüngliche Programm?) 12. Der erweiterte Euklidische Algorithmus, 15 Punkte (a) Schreiben Sie ein Python-Programm testGGT, das die Ausgabe t, r, s des erweiterten Euklidischen Algorithmus überprüft, ob sie eine gültige Ausgabe zur Eingabe a, b ist (und ob t somit der größte gemeinsame Teiler von a und b ist). (b) Der größte gemeinsame Teiler t von a und b hat die Eigenschaften: i. t teilt a, und t teilt b, (d.h. t ist ein gemeinsamer Teiler von a und b). ii. Jeder gemeinsame Teiler d von a und b teilt auch t. Ist die ganze Zahl t für die Eingabe a = 7 und b = 11 durch diese beiden Bedingungen eindeutig charakterisiert? (c) Wenn man die beiden obigen Bedingungen als Richtschnur nimmt, was müsste dann ggT(a, 0) sein? Was müsste ggT(0, 0) sein? 1 http://www.inf.fu-berlin.de/lehre/SS16/ALP2/schaltjahr.py 4 (d) Schreiben Sie eine Python-Funktion zur Berechnung von ggT(a, b) für beliebige ganze Zahlen a, b. mit dem erweiterten Euklidischen Algorithmus. Das Ergebnis t soll ≥ 0 sein. (Natürlich dürfen Sie nicht einfach eine Bibliotheksfunktion zu Hilfe nehmen.) Aufruf: t,r,s = ggT(a,b). 13. Turm von Hanoi, 15 Punkte Ergänzen Sie folgendes rekursive Programm2 zur Lösung des Hanoi-Problems: def Hanoi(n, Start, Hilfsstift, Ziel): """Turm von Hanoi Bewege n Scheiben, die zu Beginn auf dem Stift "Start" sind, zum Stift "Ziel"; "Hilfsstift" ist der dritte Stift.""" if n==0: return Hanoi(n-1, Start, Ziel, Hilfsstift) setze_um(n, Start, Ziel) Hanoi(n-1, Hilfsstift, Start, Ziel) (a) Verwalten Sie die Scheiben auf den Stiften, indem Sie geeignete Datenstrukturen verwenden. Diese Struktur soll eine globale Variable mit dem Namen Stifte sein. insbesondere soll die Prozedur setze_um(n, x, y) die Scheibe n von Stift x auf Stift y umsetzen, und dabei soll überprüft werden, ob die Regeln eingehalten werden.3 Andernfalls soll das Programm eine Fehlermeldung ausgeben und mit der Anweisung raise RuntimeError eine Ausnahme erzeugen. (b) Die Initialisierung init_Hanoi(n, x, y, z) soll die drei Stifte erzeugen und n Scheiben auf den ersten Stift setzen. Die Benennung x, y, z der Scheiben soll dabei frei wählbar sein. Ein Beispiel eines Initialisierungsaufrufs wäre: init_Hanoi(10, ’A’, ’B’, ’C’) (c) Eine Funktion anzeigen() soll den Zustand der Stifte anzeigen. (d) Eine Boolesche Funktion fertig() soll überprüfen, ob das Ziel erreicht ist. 14. Iterative Lösung des Hanoi-Problems, 0 Punkte Beweisen Sie: (a) Wenn n gerade ist und die Lösung mit dem Aufruf Hanoi(n, ’A’, ’B’, ’C’) gestartet wird, dann bewegen sich die Scheiben mit geraden Nummern immer nur zyklisch in “aufsteigender” Richtung A → B → C → A, und die Scheiben mit ungeraden Nummern in der umgekehrten Richtung. (b) Es wird immer abwechselnd Scheibe 1 und eine andere Scheibe bewegt. (c) In jeder möglichen Konfiguration gibt es höchstens drei Züge, die den Regeln entsprechen. Mit Hilfe dieser Beobachtungen kann man die Folge der Bewegungen mit einem Programm bestimmen, das ohne Rekursion auskommt. 15. Langsamste Lösung des Hanoi-Problems, 0 Punkte, zum Tüfteln Lösen Sie das Problem, die Scheiben von Stift A auf Stift C zu bringen, indem Sie möglichst viele Schritte machen, ohne dass sich jedoch eine Konfiguration wiederholt. Können Sie alle möglichen Konfigurationen durchlaufen? Ist die Lösung eindeutig? 2 Siehe http://www.inf.fu-berlin.de/lehre/SS16/ALP2/Hanoi.py Es darf immer nur die oberste Scheibe auf einem Stift bewegt werden, und sie darf nicht auf einer kleineren Scheibe landen. 3 5 Algorithmen und Programmierung 2, SS 2016 — 3. Übungsblatt Abgabe bis Freitag, 6. Mai 2016, 12:00 Uhr. Aufgabe 18b korrigiert 16. Transposition einer Matrix, 10 Punkte (a) Wir können eine (m × n)-Matrix (mit m, n ≥ 0) in Python zeilenweise“ als ” eine Liste von m Listen der Länge n darstellen, also zum Beispiel die Matrix 1 2 5 A = als A=[[1,2,5],[4,3,7]]. Die Funktion transpose soll die 4 3 7 transponierte (n × m)-Matrix AT bestimmen, bei der Zeilen mit Spalten vertauscht sind. Schreiben Sie die Vor- und Nachbedingung der Zuweisung B = transpose(A) so, dass die Funktion transpose dadurch möglichst genau spezifiziert wird. Denken Sie auch an die Prüfung, ob A eine gültige Eingabe ist: type(A) = list ∧ (∀z ∈ A : type(z) = list) ∧ . . . (b) (0 Punkte, freiwillig) Implementieren Sie die Funktion transpose in Python. 17. Ägyptische Multiplikation, 10 Punkte Die nebenstehende Funktion multipliziert zwei nichtnegative ganze Zahlen. def mul(a0,b0): a,b = a0,b0 s = 0 while a!=0: if a%2==1: s = s+b a = a//2 b = b*2 return s (a) (0 Punkte) Führen Sie den Algorithmus mit einigen speziellen und allgemeinen Beispielen per Hand (oder mit Computerunterstützung) durch, bis Sie ihn verstehen. (b) Finden Sie eine möglichst aussagekräftige Schleifeninvariante, aus der die Korrektheit des Algorithmus ersichtlich ist. Sie können sich dabei auf die Ausgangswerte a0 , b0 der Parameter beziehen. (c) (Programmieraufgabe, im KVV hochzuladen) Fügen Sie Ihre Invariante als assert-Zusicherung am Schleifenanfang ein, und testen Sie sie. (d) Funktioniert das Verfahren auch, wenn a < 0 ist? Was passiert, wenn a nicht ganzzahlig ist? Wie ist es bei b? Begründen Sie Ihre Antworten. 18. Hoare-Kalkül, 10 Punkte Beweisen Sie folgende Aussagen, oder finden Sie Belegungen der Variablen, sodass die Vorbedingungen erfüllt sind, aber nach der Ausführung des Programms nicht die Nachbedingungen. Geben Sie bei den Beweisen alle benützten Regeln an. (a) (c) (d) (e) (f) { } a=x; b=y; b=b+a { b = x + y } (b) { } u=a-5/2; v=u*2-1/2 { v ≥ 0 } { x = as + r } a=a+1; r=r-s { x = as + r } { a ∈ Z } x=a-5/2; z=x**2-1/4 { z ≥ 0 } { 0 ≤ x < t } t=t//2; x=x-t { x ≤ t } (0 Punkte) { a = x ∧ b = y } a-=b; b+=a; a-=b { a = −y ∧ b = x } 19. (0 Punkte) Schreiben Sie eine Funktion is_sorted(L), die für eine Liste L kontrolliert, ob die Elemente der Liste sortiert sind. Das Ergebnis ist ein Paar (Auf,Ab). Auf = 1, wenn die Liste aufsteigend sortiert ist, aber Auf = 2, wenn sie sogar streng aufsteigend sortiert ist, das heißt, wenn alle Elemente verschieden sind; sonst ist Auf = 0. Ab ist das analoge Ergebnis für absteigende Sortierung. (Kann eine Liste überhaupt sowohl aufsteigend als auch absteigend sortiert sein?) 6 Algorithmen und Programmierung 2, SS 2016 — 4. Übungsblatt Abgabe bis Freitag, 13. Mai 2016, 12:00 Uhr 20. Elimination von continue, 8 Punkte Schreiben Sie zu den beiden folgenden Beispielen äquivalente Programme ohne die Anweisungen break und continue und ohne die else-Klausel bei der while-Schleife. while B: S if C: T if D: U else: continue V W while B: S if C: T else: U break V else: W Dabei sind B, C, D Bedingungen, und S, T, . . . steht für Anweisungen oder Folgen von Anweisungen. (Die else-Klausel der while-Schleife wird ausgeführt, wenn die Schleife wegen der Bedingung B verlassen wird und nicht durch die break-Anweisung.) 21. Ägyptische Multiplikation, 14 Punkte (a) Beweisen Sie die partielle Korrektheit der ägyptischen Multiplikation (Aufgabe 17 vom 3. Blatt) mit dem Hoare-Kalkül unter der Annahme a0 , b0 ≥ 0. (Sie dürfen dabei das Programm leicht umschreiben, ohne die Bedeutung zu ändern; zum Beispiel können Sie bei der Anweisung a=a//2 eine Fallunterscheidung machen, ob a gerade oder ungerade ist.) Geben Sie jedesmal an, welche Regeln des Hoare-Kalküls Sie verwenden. (b) Beweisen Sie die totale Korrektheit. 22. Suche, 8 Punkte, Programmieraufgabe (a) Schreiben Sie eine einfache Python-Funktion finde nach der folgenden durch Vor- und Nachbedingungen ausgedrückten Spezifikation. Verwenden Sie nicht einfach die eingebaute Methode (Funktion) index oder die Abfrage n in A“. ” { type(A) = list } i = finde(A,n) { (type(i) = int ∧ A[i] = n) ∨ (i = None ∧ ∀j : 0 ≤ j < len(A) ⇒ A[j] 6= n) } (b) Schreiben Sie die Funktion so um, dass sie nur mit while-Schleifen auskommt. (Eventuell müssen Sie in einer späteren Aufgabe die Korrektheit des Programms beweisen.) 23. Tribonacci-Zahlen, 0 Punkte Erweitern Sie die Funktion Tr(n) aus der Vorlesung vom 25. April1 so, dass sie die Tribonacci-Zahlen Tn auch für auch für negative Parameter n berechnet (und nicht bloß 0 ausgibt). Das Ergebnis soll so definiert sein, dass die Gleichung Tn+1 = Tn + Tn−1 + Tn−2 für alle ganzen Zahlen n gilt. 1 http://www.inf.fu-berlin.de/lehre/SS16/ALP2/tribonacci.py 7 Algorithmen und Programmierung 2, SS 2016 — 5. Übungsblatt Abgabe bis Freitag, 20. Mai 2016, 12:00 Uhr 24. Arithmetische Ausdrücke, 12 Punkte, Programm berechne aus der Vorlesung1 (a) (4 Punkte) Kann der Stapel bei der Auswertung eines arithmetischen Ausdrucks zwischendurch jemals den Inhalt ( 1 + 2 * ( 3 + 4 * ( haben? Kann er jemals den Inhalt 1 + 2 * 3 (ohne Klammern!) haben? Wie ist es mit den Inhalten ( 1 * * ( und ( 1 * 2 + ( 3 * 4 + ( ? Wenn ja, zeigen Sie eine Eingabe, die zu diesem Inhalt führt, wenn nein, begründen Sie Ihre Antwort. (b) (4 Punkte) Gibt es ungültige arithmetische Ausdrücke, die nicht zu einer Fehlermeldung führen? (Wie bei allen Aufgaben ist die Antwort zu begründen.) (c) (4 Punkte) Das Programm wurde in der Vorlesung ohne großes Nachdenken entwickelt. Argumentieren Sie mit Hilfe einer passenden Schleifeninvariante (zum Beispiel: Wie kann der Stapelinhalt aussehen?), dass das Programm korrekt ist, oder finden Sie ein Beispiel, bei dem das Programm versagt. 25. Partielle Korrektheit, 8 Punkte (a) (4 Punkte) Die folgende Funktion2 verteilt die Elemente von zwei Listen neu. Beweisen Sie mit dem Hoare-Kalkül, dass die beiden Listen am Ende dieselbe Summe haben, wenn das Programm für diese Eingabelisten terminiert. def Ausgleich(s1, s2): """Gleiche zwei Listen aus, sodass sie die gleiche Summe haben. s1 und s2 sind Listen mit positiven ganzen Zahlen.""" assert all(x>0 and isinstance(x,int) for x in s1+s2) while sum(s1)!=sum(s2): if sum(s1) > sum(s2): a = s1.pop() s2.append(a) else: a = s2.pop() s1.append(a) return s1, s2 (b) (4 Punkte) Warum ist in dem Programm trotzdem der Wurm drin? 26. Endrekursion, Programmieraufgabe, 10 Punkte Eliminieren Sie die Rekursion aus folgender Funktion3 ohne Verwendung eines Stapels. Gehen Sie dabei schematisch nach der Methode aus der Vorlesung vor. Erstellen Sie als Zwischenschritt eine Lösung mit goto-Anweisungen, bevor Sie als Endprodukt ein gültiges Python-Programm schreiben. def fak(n, s=1): if n<0: raise ValueError("negatives Argument",n) if n==0: return s return fak(n-1, s*n) 1 http://www.inf.fu-berlin.de/lehre/SS16/ALP2/ausdruecke.py http://www.inf.fu-berlin.de/lehre/SS16/ALP2/Ausgleich.py 3 http://www.inf.fu-berlin.de/lehre/SS16/ALP2/Fakultaet.py 2 6 Algorithmen und Programmierung 2, SS 2016 — 6. Übungsblatt Abgabe bis Freitag, 27. Mai 2016, 12:00 Uhr Liebe Studierende! Das KVV-System bietet die Möglichkeit, dass Sie die Lösungen Ihrer Mitstudierenden bewerten und durch das Lesen von anderen Lösungen und den Vergleich mit Ihrer eigenen Lösung ein andersartiges Lernerlebnis erfahren. Wir wollen das bei Aufgabe 28 zum ersten Mal ausprobieren. Ihre Teilname am Bewertungsverfahren wird mit Übungspunkten honoriert. In der Vorlesung am Mittwoch, den 25. Mai erfahren Sie genaueres zum Ablauf, und es wird auch schriftliche Anleitungen und Hilfestellungen geben. 27. Objekte und Klassen, 8 Punkte Definieren Sie folgende Begriffe in 1–2 Sätzen, und illustrieren Sie sie jeweils mit einem kurzes Programmbeispiel in Python. (a) Objekt (Exemplar) einer Klasse (c) Aufruf einer Methode (b) Vererbung (d) Unterklasse und Oberklasse (c) Überschreiben von Attributen und Methoden 28. Wettrennen, 15 Punkte (a) Definieren Sie eine Unterklasse Verfolger von Turtle, die bei der Konstruktion eine Zielschildkröte als Parameter erhält: t=Verfolger(ziel). Mit der Methode t.verfolge(Schrittweite) soll t einen Schritt der Länge Schrittweite auf das Ziel zu machen. (b) Definieren Sie eine Klasse Rechteck mit dem Konstruktionsaufruf Rechteck(l,r,u,o) und einer Methode zeichne, die das Rechteck zeichnet. (c) Verändern Sie die Klasse Wegläufer aus der Vorlesung, sodass sie bei der Konstruktion ein Rechteck als zusätzlichen optionalen Parameter Begrenzung akzeptiert: def __init__(self, wovor, Begrenzung=None): Die Methode lauf_weg soll so modifiziert werden, dass die Schildkröte das Rechteck nicht verlässt, sich aber dabei innerhalb der Schrittweite möglichst weit weg vom gefürchteten Verfolger bewegt. Sie müssen den letzen Satz dieser Vorgabe nicht buchstäblich umsetzen; aber die Schildkröte darf nicht einfach stehenbleiben, wenn sie am Rand der Begrenzung ist. Spezifizieren Sie genau, was Ihre Wegläufer-Schildkröte in jedem Fall macht. Schreiben Sie die Spezifikation als Kommentar in Ihr Programm. Vorerst sollen Sie bei der Bearbeitung dieser Aufgabe bloß folgendes beachten: Die Bewertung soll anonym erfolgen; schreiben also keine Namen in die Dateien, und nennen Sie Ihr Programm bloß Aufgabe27.py, abweichend von den generellen Vorgaben. Kommentieren Sie besonders gut, damit Ihr Programm angenehm zu begutachten ist. 29. Knobelaufgabe, 0 Punkte Stellen Sie mit den Zahlen 1, 5, 6, 7 und den Grundrechnungsarten +, −, ×, / die Zahl 21 dar. Jede Zahl muss genau einmal verwendet werden. Klammern sind erlaubt, aber Tricks, wie zum Beispiel 15 aus 1 und 5 zu bilden, sind verboten.1 1 Die meisten von Ihnen werden das Problem schneller mit dem Computer durch Probieren aller Möglichkeiten lösen als per Hand. 7 30. (a) Jäger und Beute, 0 Punkte Ein Adler A mit Geschwindigkeit 2 möchte möglichst schnell zwei Spatzen S1 und S2 fangen. Die Spatzen fliegen stets mit konstanter Geschwindigkeit 1 von der aktuellen Position des Adlers weg. Der Adler könnte zunächst gradlinig den näheren der beiden Spatzen fangen und von dort aus geradlinig auf den anderen zusteuern. Schreiben Sie ein Programm, dass dieses Verhalten mit kleinen Zeitschritten simuliert und graphisch visualisiert. (b) (Forschungsaufgabe, Antwort unbekannt) Ist diese Strategie immer optimal? Können Sie Ausgangspositionen finden, wo es eine schnellere Strategie gibt? 31. Objekte und Referenzen, 7 Punkte Was ist die Ausgabe von untenstehendem Programm? Erklären Sie, was passiert. Wie kann man am Ende auf das Element ’88’ zugreifen? def f1(a): a = a + [a] def f2(a): b = a b.append(7) def f3(a): b = a + [’88’] a.append([a,b]) a=[4,5,6] f1(a) print(a) f2(a) print(a) f3(a);print(a) 32. Elimination von goto-Anweisungen, 0 Punkte Das nebenstehende Programm wurde 1962 in der Programmiersprache Algol 60 veröffentlicht2 und von mir fast eins-zu-eins nach Python übersetzt. Das Programm funktioniert so: Each call of PERM changes the order of the first n components of x, and n! successive calls will generate all n! permutations. The parameter ‘first’ must be True when PERM is first called, to cause proper initialization. Successive calls must leave first = False. On exit from the (n!)-th call of PERM, the function returns True. Obwohl es damals schon for- und while-Schleifen gab, sehen Sie noch jede Menge goto-Anweisungen. def PERM(x,n,first=False): global p,d done = False if first then initialize: p = (n+1)*[0] d = (n+1)*[1] k=0 INDEX: p[n] = q = p[n] + d[n] if q == n: d[n] = -1 go to LOOP if q != 0: go to TRANSPOSE; d[n] = 1 k = k + 1 LOOP: if n > 2: n = n - 1 go to INDEX Final exit: q = 1 done = True TRANSPOSE: q = q + k x[q-1],x[q] = x[q],x[q-1] return done "end PERM;" (a) Bringen Sie das Programm in eine vernünftige Struktur. Den Bezug auf globale Variablen und die Aufrufkonventionen können Sie unverändert lassen. (b) Verstehen Sie, was das Programm macht. 2 H. F. Trotter, Algorithm 115: PERM, Communications of the ACM 5 (1962), 434–435. doi:10.1145/368637.368660, siehe http://www.inf.fu-berlin.de/lehre/SS16/ALP2/PERM.py 8 Algorithmen und Programmierung 2, SS 2016 — 7. Übungsblatt Abgabe bis Freitag, 3. Juni 2016, 12:00 Uhr 33. Bubblesort, Programmieraufgabe, 10 Punkte Betrachten Sie folgendes Python-Programm, das eine Liste mit Zahlen bekommt und die Zahlen innerhalb dieser Liste aufsteigend sortiert. Verwandeln Sie die forSchleife in eine while-Schleife und überlegen Sie sinnvolle Invarianten für beide while-Schleifen, aus denen die Korrektheit des Sortierprogramms hervorgeht. Warum muss man die Sortierung nur bis Ende überprüfen? Testen Sie Ihre Invarianten durch Einfügen von assert-Anweisungen an allen relevanten Stellen. Sie dürfen innerhalb der assert-Anweisungen selbstdefinierte Hilfsfunktionen verwenden wie zum Beispiel eine Funktion, die überprüft, ob eine Liste sortiert ist (Aufgabe 19). def bubblesort (A): Ende = len(A)-1 while Ende>0: letzteÄnderung = 0 for i in range(Ende): if A[i]>A[i+1]: A[i], A[i+1] = A[i+1], A[i] letzteÄnderung = i Ende = letzteÄnderung 34. O-Notation, 5 Punkte Gegeben sind zwei positive Funktionen S, T : N → R>0 . Beweisen Sie: Aus S(n) = O(f (n)) und T (n) = O(g(n)) folgt, dass S(n) · T (n) = O(f (n)g(n)) ist. 35. Vergleich von asymptotischen Laufzeiten, 0 Punkte Welche der beiden folgenden Laufzeiten f (n) und g(n) ist für große Werte von n schneller, und welche für kleine n? Bei welchem Wert von n ändert sich die Antwort? (a) f (n) = 10n(log2 n)2 , g(n) = 2n3/2 (b) f (n) = 5 · 2n , g(n) = 100n2 log2 n 36. Laufzeitvergleich, 10 Punkte Die folgende Tabelle gibt die Laufzeiten von verschiedenen Algorithmen für eine Eingabe der Größe n auf einem bestimmten Rechner an. Verfahren Algorithmus Algorithmus Algorithmus Algorithmus A B C D Laufzeit in Millisekunden 0,001n! 0,01n2 0,1 n log2 n 0,5n Programmieraufwand 1 Stunde 1 Tag 1 Woche 10 Wochen Welchen Algorithmus würden Sie empfehlen, wenn das Programm für Eingaben der Größe (a) n = 10, (b) n = 1000, (c) n = 10 000 000 bis zum Jahr 2025 (i) einmal pro Jahr, (ii) täglich, (iii) 10-mal pro Sekunde laufen müsste. • (5 Punkte) Untersuchen Sie die Szenarien (b)+(iii), (a)+(i), und (c)+(ii). • (5 Punkte) Was ändert sich für diese Szenarien, wenn man auf eine zehnmal schnellere Hardware umsteigt? 37. Wettrennen (5 Punkte): Bewerten Sie zwei Lösungen von Aufgabe 28 im KVV. 9 Algorithmen und Programmierung 2, SS 2016 — 8. Übungsblatt Abgabe bis Freitag, 10. Juni 2016, 12:00 Uhr 38. Stabile Sortierverfahren, 10 Punkte Ein Sortieralgorithmus ist stabil, wenn es gleiche Werte in derselben relativen Reihenfolge belässt, in der sie in der Eingabe stehen. Bewerten Sie die Verfahren (i) Bubblesort (ii) Sortieren durch Verschmelzen (iii) Sortieren durch Einfügen und (iv) Quicksort bezüglich ihrer Stabilität. Es stehen folgende Antworten zur Auswahl. (a) Man kann gar nicht vermeiden, dass das Verfahren stabil ist. (b) Das Verfahren ist stabil, wenn man bei der Programmierung darauf achtet. (c) Das Verfahren kann nur mit Mühe stabil gemacht werden. Begründen Sie Ihre Antworten. Im Fall (b) geben Sie Beispiele, was man falsch machen kann. Im Fall (c) geben Sie ein Beispiel, wo der Algorithmus nicht stabil ist. 39. Umrechnungstabelle, Programmieraufgabe, 10 Punkte (8 Punkte) Schreiben Sie ein Programm, das bei Eingabe einer Währung und eines Wechselkurses eine schlaue Umrechnungstabelle für eine Auslandsreise nach folgendem Muster berechnet, und das ohne Sortieren auskommt. Alle Beträge, die zwischen 1 und 100 Euro ausmachen und die in einer der Währungen eine “runde Zahl” ergeben, sollen darin enthalten sein. (Die Aufteilung in drei Spalten ist nicht notwendig.) 313.81 500.00 627.62 1000.00 1569.06 HUF HUF HUF HUF HUF = = = = = 1.00 1.59 2.00 3.19 5.00 EUR EUR EUR EUR EUR 2000.00 3138.12 5000.00 6276.24 HUF HUF HUF HUF = 6.37 EUR = 10.00 EUR = 15.93 EUR = 20.00 EUR 10000.00 15690.60 20000.00 31381.20 HUF HUF HUF HUF = 31.87 = 50.00 = 63.73 = 100.00 EUR EUR EUR EUR (2 Punkte) Wieviele Einträge enthält die Tabelle höchstens und mindestens? (Zusatzfrage, 0 Punkte.) Bei welchem Wechselkurs wird die größte relative Lücke (das heißt, das Verhältnis zwischen aufeinanderfolgenden Einträgen) am kleinsten? 40. Logarithmen, 0 Punkte. In der Vorlesung wurde gezeigt, dass Quicksort im durchschnittlichen Fall weniger als 2(n+1)·Hn ≤ 2(n+1)(1+ln n) = O(n log n) Vergleiche benötigt. Warum muss man beim Logarithmus in der O-Notation nicht angeben, zu welcher Basis der Logarithmus gemeint ist? 41. Sortieren durch Verschmelzen von klein auf“ (bottom-up mergesort), 10 Punkte ” Bei diesem Verfahren wird nicht rekursiv zerlegt, sondern es werden immer längere sortierte Teillisten aufgebaut. Nach i Schritten hat man Teillisten der Länge 2i , bis auf eine letzte Liste, die möglicherweise kürzer ist. Diese Listen werden dann paarweise zusammengefasst und verschmolzen. (a) Untersuchen Sie die exakte Anzahl der Vergleiche im schlimmsten Fall für dieses Verfahren und für das klassische Sortieren durch Verschmelzen nach dem Teileund-herrsche-Prinzip: i. Für Eingaben der Länge 2k , für k = 2, 3, 4, 5. ii. Für Eingaben der Länge 3 · 2k−1 , für k = 2, 3, 4, 5. iii. Für Eingaben der Länge 2k+1 − 1, für k = 2, 3, 4, 5. (b) Beweisen Sie, dass die Laufzeit dieses Verfahrens O(n log n) ist. Diese Aufgabe ist für die wechselseitige Bewertung vorgesehen. Laden Sie die Lösung daher im KVV hoch, vorzugsweise als PDF-Datei oder ersatzweise als gescannte Bilddatei (nicht als veränderbare doc-, tex-, rtf- oder odt-Datei), und schreiben Sie Ihre Namen nicht in die Datei. Eine Abgabe auf Papier ist nicht notwendig. 10 Algorithmen und Programmierung 2, SS 2016 — Probeklausur Abgabe bis Mittwoch, 8. Juni 2016, 13:45 Uhr 42. Rekursion, 10 Punkte Die folgende Funktion berechnet die Anzahl der Binärziffern (ohne führende Nullen) einer natürlichen Zahl n. (Beispiel: n = 23 = (10111)2 7→ 5) def binaryDigits(n): if n==0: return 0 else: return 1 + binaryDigits(n//2) (a) Schreiben Sie für diese Aufgabe eine Funktion ohne Rekursion. (b) Schreiben Sie für diese Aufgabe eine Funktion ohne Division (und ohne irgendwelche Bibliotheksfunktionen). (Sie können Teil (a) und (b) auch gemeinsam durch eine Funktion lösen.) (c) Geben Sie die Laufzeit und den Speicherbedarf für die gegebene Funktion und für Ihre Lösungen an. (Nehmen Sie dabei an, dass jede der vier Grundrechnungsarten (+, −, ∗ und //) mit ganzen Zahlen in O(1) Zeit durchgeführt werden kann.) 43. O-Notation, 10 Punkte (a) Gegeben sind zwei positive Funktionen S, T : N → R>0 . Beweisen Sie: Aus S(n) = Ω(f (n)) und T (n) = Ω(g(n)) folgt, dass S(n) · T (n) = Ω(f (n)g(n)) ist. (b) Geben Sie einen möglichst einfachen Ausdruck der Form Θ(f (n)) für folgenden Ausdruck an: 25n2 − 100bn/2c + 365 (c) Geben Sie einen möglichst einfachen Ausdruck der Form Θ(f (n)) für folgenden Ausdruck an: 23n + 12n log2 n + 1666 3. siehe Rückseite 11 44. Quicksort, 10 Punkte Das folgende Python-Programm ordnet einen Abschnitt a[l : r] einer Liste a um und zerlegt ihn in drei Teile a[l : i − 1], a[i], und a[i + 1 : r], sodass alle Elemente im ersten Teil ≤ a[i] sind und alle Elemente im dritten Teil ≥ a[i] sind. def zerlege(a,l,r): """Zerlege a[l:r]=a[l],a[l+1],...,a[r-1] für Quicksort Voraussetzung 1: l<r Voraussetzung 2: a[r] existiert, und a[r] >= a[i] für l<=i<r. Rückgabewert = i = Position des Pivotelements """ pivot = a[l] # a[l] wird als Pivotelement gewählt. k, g = l+1, r-1 while True: _______________________________________________________ (1.) ____________________________________________________________ while a[k]<pivot: k = k+1 while a[g]>pivot: g = g-1 _______________________________________________________ (2.) ____________________________________________________________ if g<=k: a[l],a[g]=a[g],pivot return g a[k],a[g] = a[g],a[k] # vertausche _______________________________________________________ (3.) ____________________________________________________________ k = k+1 g = g-1 (a) Fügen Sie an den nummerierten Stellen aussagekräftige Invarianten ein, aus denen die Korrektheit des Programmes hervorgeht. Sie können dabei mathematische Notation verwenden und brauchen keine Python-Syntax zu beachten. Zum Beispiel könnte die erste Invariante ungefähr so aussehen: (1.) pivot = a[l] ∧ k > l ∧ ∀i : (. . . ) ⇒ a[i] ≤ pivot ∧ . . . (b) Beweisen Sie, dass das Programm auf keine Listenelemente außer auf a[l], a[l + 1], . . . , a[r] zugreift. Sie können dabei auf Ihre Invarianten von Teil (a) zurückgreifen. (Die Gültigkeit dieser Invarianten brauchen Sie nicht zu beweisen.) (c) (3 Zusatzpunkte) Beweisen Sie, dass das Programm terminiert. 12 Algorithmen und Programmierung 2, SS 2016 — 10. Übungsblatt Abgabe bis Freitag, 17. Juni 2016, 12:00 Uhr 45. Klassen, Exemplare, und Attribute, 12 Punkte Wir erzeugen zwei Exemplare einer Klasse C, die keine eigenen Attribute hat: class C: pass -- "leere" Klasse exemplar1 = C() exemplar2 = C() (a) Was passiert, wenn das Objekt exemplar1 danach ein neues Attribut erhält: exemplar1.a = 2“. Hat das Auswirkungen auf exemplar2? Auf die Klasse C? ” (b) Was passiert, wenn stattdessen die Klasse C danach ein neues Attribut erhält: C.a = 4“. Hat das Auswirkungen auf exemplar1 und exemplar2? Auf Exem” plare von C, die danach erzeugt wurden? (c) Wie ist es, wenn sowohl (a) als auch (b) für eine Attribut mit dem gleichen Namen a stattfindet? Kommt es dabei auf die Reihenfolge an? Erklären Sie, was passiert. 46. Haldensortieren, Programmieraufgabe, 12 Punkte (a) Ergänzen Sie die Funktionen zugroß(a,i) und zuklein(a,i) aus der Vorlesung zu lauffähigen Python-Funktionen. (b) Implementieren Sie damit eine Funktion heapsort(a,n), die die Elemente von a[:n] aufsteigend sortiert. 47. O-Notation, 6 Punkte Geben Sie möglichst einfache obere Schranken der Form O(f (n)) und untere Schranken der Form Ω(g(n)) für folgende Funktionen an: (a) 2blog2 nc (b) 3blog2 nc dlog2 log2 ne (c) 22 48. Laufzeit der Haldenkonstruktion, 0 Punkte Beweisen Sie (zum Beispiel durch vollständige Induktion nach k, oder indem Sie den Ausdruck auf der linken Seite für k und k − 1 hinschreiben und die Differenz bilden): Pk i k+1 − k − 2 i=0 2 (k − i) = 2 49. Asymptotisches Wachstum, 0 Punkte (a) Beweisen Sie: 125n3 + 2n = Θ(2n ) (Es gilt sogar für alle a und für alle b > 1: na + bn = Θ(bn ). Exponentialfunktion schlägt jede Potenz.) Anleitung zu einer möglichen Lösung: 1. Für n ≥ 125 gilt 125n3 ≤ n4 . 2. Die Funktion eu−1 − u ist für u ≥ 1 monoton wachsend. (Ableitung!) 3. Daraus ergibt sich 16u−1 ≥ eu−1 ≥ u für u ≥ 1, und somit 16u ≤ 16u . 4. Für n = 16u ist somit n ≤ 2n/4 und n4 ≤ 2n , falls n groß genug ist. 5. Es gibt eine Schranke n0 , sodass 125n3 ≤ 2n für alle n ≥ n0 gilt. (b) Beweisen Sie: (log2 n5 )6 + n2 = Θ(n2 ). (Es gilt sogar für alle a, c und für alle b > 0: c(log2 n)a + nb = Θ(nb ). Potenz schlägt jeden Logarithmus.) 50. (10 freiwillige Zusatzpunkte): Bewerten Sie zwei Lösungen von Aufgabe 41 im KVV. 13 Algorithmen und Programmierung 2, SS 2016 — 11. Übungsblatt Abgabe bis Freitag, 24. Juni 2016, 12:00 Uhr 51. Verkettete Listen, Programmieraufgabe, 15 Punkte Modifizieren Sie die Klasse VerketteteListe1 aus der Vorlesung so, dass sie folgende zusätzliche Methoden unterstützt. (a) public void einfüge(int i, Knoten k). Fügt einen neuen Knoten mit Wert i an die Stelle nach dem Knoten k in die Liste ein. (b) public boolean lösche(). Löscht den ersten Knoten aus der Liste. Wenn die Liste vorher leer war, soll das Ergebnis false sein, sonst true. (c) public boolean lösche(Knoten k). Löscht den Knoten k aus der Liste. Wenn der Knoten k gar nicht zur Liste gehört, soll das Ergebnis false sein, sonst true. (Diese Operation geht nicht in konstanter Zeit.) Geben Sie dem Programm den Namen Aufgabe51 Nachname1 Nachname2.java. Die obligatorische Testsuite soll als main-Methode der Klasse VerketteteListe ablaufen. 52. Arithmetik in Java, 15 Punkte Arithmetische Operationen werden in Java strikt von links nach rechts ausgerechnet, sofern Vorrangregeln und Klammern nichts anderes vorgeben. Wenn beide Operanden einer arithmetischen Operation vom Typ int, long, float oder double sind, hat das Ergebnis denselben Typ; bei gemischten Operanden wird ein Operand vorher in den allgemeineren“ Typ umgewandelt (int → long → float → double). Gleit” kommakonstanten wie 0.1 werden immer als double-Werte interpretiert. Werte vom Typ int liegen im Intervall [−231 , 231 − 1], und ganzzahlige Aritmetik mit solchen Werten wird immer modulo 232 ausgeführt. P Die folgende Funktion zeigt acht verschiedene Arten, die Summe ni=1 1/i2 zu berechnen, die für n → ∞ (langsam) gegen π 2/6 konvergiert. static double Summe(int double s = 0.0; float eins = 1; for (int i=1; i<=n; s += s += s += s += Varianten s += s += s += s += return s; } n) { i++) 1/i*i; 1/(i*i); 1.0/i/i; 1.0/(i*i); 1.0/((double)(i)*i); 1/(double)(i*i); eins/i/i; 1/(1.0*i*i); (1) (2) (3) (4) (5) (6) (7) (8) Testen Sie jede einzelne dieser Varianten jeweils für n = 100, 10 000, 106 , 108 und 109 und vergleichen Sie das Ergebnis mit dem Grenzwert. Erklären Sie, was passiert.2 Formulieren Sie eine Vermutung, wie groß ungefähr der Abstand zwischen dem Grenzwert und der (exakten) Summe der ersten n Glieder ist. 1 2 http://www.inf.fu-berlin.de/lehre/SS16/ALP2/VerketteteListe.java https://de.wikipedia.org/wiki/IEEE_754#Unendlich 14 Algorithmen und Programmierung 2, SS 2016 — 12. Übungsblatt Abgabe bis Freitag, 1. Juli 2016, 12:00 Uhr (Aufgabe 53 korrigiert am 7. Juli) 53. Verkettete Liste mit Zeiger zum Listenende, Programmieraufgabe, 10 Punkte Erweitern Sie die Klasse VerketteteListe von Aufgabe 51, indem Sie eine Unterklasse VerketteteSchlange erstellen, die die folgenden zusätzlichen Operationen in konstanter Zeit unterstützt. (a) public void anhängen(int i). Fügt einen neuen Knoten mit Wert i am Ende der Liste ein. (b) public void anhängen(VerketteteSchlange L2). Hängt die verkettete Liste L2 an das Ende der Liste an und vereinigt somit die beiden Listen zu einer Liste. Die neue Klasse soll auch alle bisherigen Operationen weiterhin unterstützen. Dazu müssen Sie gegebenenfalls die alten Methoden überschreiben. Verwenden Sie dabei möglichst viele Methoden der ursprünglichen Klasse, indem Sie zum Beispiel etwa super.einfüge() aufrufen, und programmieren Sie nur das neu, was nötig ist. Achten Sie darauf, dass die Operationen auch bei leeren Listen korrekt funktionieren. 54. Zusatzfrage, 0 Punkte Kann man die Liste L2 noch verwenden, nachdem sie in der Methode anhängen von Aufgabe 53b verbraucht“ worden ist? ” 55. Turm von Hanoi, Programmieraufgabe, 10 Punkte Schreiben Sie ein Java-Programm, das den Turm von Hanoi löst. Verwenden Sie dafür die Klasse hanoi.Turm aus dem Paket hanoi1 Die drei Stifte sind mit ’a’, ’b’, und ’c’ bezeichnet. Das Programm TestTurm2 zusammen mit der Klasse HanoiLoesung3 zeigt ein Beispiel, wie die Klasse Turm verwendet wird. In der Klasse Turm sind folgende Methoden implementiert (siehe Aufgabe 58b für die Bedeutung von final“): ” package hanoi; public class Turm { public Turm(int [] anfangsturm) throws TurmException /** anfangsturm enthält die Größen aller Scheiben, aufsteigend sortiert. Gleiche Scheiben sind erlaubt. Die Scheiben werden auf Stift ’a’ gesetzt. */ public final int setzeUm(char s, char z) throws TurmException; { . . . /** bewegt die oberste Scheibe vom Stift s (Start) zum Stift z (Ziel). Vorbedingungen: • s, z = ’a’, ’b’, oder ’c’, und s 6= z. • Stift s ist nicht leer. • Oberste Scheibe d auf Stift s ≤ oberste Scheibe auf z, oder z ist leer. Der Rückgabewert ist d. */ } public final boolean fertig(); // testet, ob die Stifte ’a’ und ’b’ leer sind public int[] anfangsturm(); // erstellt eine Kopie des Ausgangsturms public final int bewegungen(); // die Anzahl der bisherigen Bewegungen } 1 Speichern Sie die Dateien Turm.class und TurmException.java von http://www.inf.fu-berlin.de/ lehre/SS16/ALP2/hanoi/ in ein Unterverzeichnis mit Namen hanoi. 2 http://www.inf.fu-berlin.de/lehre/SS16/ALP2/TestTurm.java 3 http://www.inf.fu-berlin.de/lehre/SS16/ALP2/HanoiLoesung.java 15 Schreiben Sie Ihre eigene Klasse HanoiLoesung, die mit der Methode anfangsturm den Turm untersucht und anschließend die korrekte Folge von Bewegungen durchführt. (Diese Klasse wird dann von einem Testprogramm aufgerufen, das ähnlich wie das Programm TestTurm arbeitet. Schreiben Sie trotzdem Ihre eigene Testsuite.) 56. Schnittstellen, 0 Punkte Schreiben sie eine Schnittstelle interface TurmInterface für Klassen, die die Methoden von Turm von Aufgabe 55 unterstützen. 57. Generische Methoden, Programmieraufgabe, 10 Punkte Außer Klassen können auch Methoden in Java mit einem Datentypen parametrisiert werden. Schreiben Sie eine generische statische Methode swap, die zwei gegebene Elemente eines Feldes miteinander vertauscht: class Swap { public static <T> void swap(...) ... Definition von swap ... public static void main(String[] args) { Integer [] a = {1,2,0,2,3}; Swap.<Integer>swap(a, 2, 3); // vertauscht a[2] mit a[3] swap(a, 0, 4); // auch ohne explizite Angabe des Datentyps // Der Typ wird aus dem Argument a erschlossen. (Typinferenz) } } 58. Endgültige Klassen, 0 Punkte (a) Studieren Sie den Quellcode Turm.java4 , der eine mögliche Implementierung der Klasse Turm zeigt. Gibt es eine Möglichkeit das Testprogramm von Aufgabe 55 zu überlisten, sodass man auch mit einer falschen Lösung erreichen kann, dass fertig() am Ende den Wert true zurückgibt? (b) Nehmen wir an, wir dürfen die Klasse Turm erweitern und die Methode HanoiLoesung.löse(t) auch mit einer Unterklasse von Turm aufrufen. Methoden, die als final“ de” klariert werden, können von einer Unterklasse nicht überschrieben werden. Gibt es trotzdem einen Weg, das Testprogramm zu überlisten? (c) Kann man auch erreichen, dass die Methode bewegungen nicht die korrekte Zahl an Aufrufen von setzeUm meldet? (d) Wie kann man diese Schlupflöcher gegebenenfalls stopfen? (e) Warum ist es nicht notwending, die Methode anfangsturm als final zu deklarieren? Warum wurde der Konstruktor Turm nicht als final deklariert? 59. Die Collections-Klassen von Java, 0 Punkte Studieren Sie die Dokumentation der Klasse PriorityQueue5 und finden Sie eine möglichst einfache Möglichkeit, mit Hilfe der Methoden und Klassen des CollectionRahmens die Elemente einer Prioritätswarteschlange pq in absteigend sortierter Reihenfolge auszudrucken, (a) wenn die Prioritätswarteschlange pq dabei nicht verändert werden soll, (b) wenn die Prioritätswarteschlange pq danach nicht mehr gebraucht wird. 4 http://www.inf.fu-berlin.de/lehre/SS16/ALP2/Turm.java siehe zum Beispiel http://docs.oracle.com/javase/8/docs/api/java/util/PriorityQueue.html, http://docs.oracle.com/javase/tutorial/collections/intro/, oder http://docs.oracle.com/javase/tutorial/collections/interfaces/queue.html. 5 16 Algorithmen und Programmierung 2, SS 2016 — 13. Übungsblatt Abgabe bis Freitag, 8. Juli 2016, 12:00 Uhr 60. Verallgemeinerter Turm von Hanoi, Programmieraufgabe, 11 Punkte. Untersuchen Sie die Variante des Turmes von Hanoi, bei dem auch gleich große Scheiben erlaubt sind. Gleiche Scheiben dürfen übereinander liegen, aber eine Scheibe darf nach wie vor nicht über einer kleineren Scheibe liegen. Überlegen Sie, wie die Lösung für den klassischen Fall hergeleitet wird und warum diese Lösung optimal ist. Verallgemeinern Sie Ihre Überlegung für den Fall gleich großer Scheiben. Schreiben Sie eine Klasse HanoiLoesungVerallgemeinert, die die optimale Bewegungsfolge für diesen Fall berechnet. Zum Beispiel soll der Aufruf int[] a = {1,2,2,2}; HanoiLoesungVerallgemeinert.löse(new Turm(a)); das Problem mit 5 Bewegungen lösen. 61. Binärbäume, Rekursion, 5 Punkte Schreiben Sie (auf Papier) ein Java-Programm, das die Anzahl der Knoten in einem Binärbaum bestimmt, der durch folgende Klasse definiert ist. class Binärbaum <T> { T wert; Binärbaum <T> links, rechts; // Kinder ... } 62. Radixsort und Sortieren durch Fachverteilung, Programmieraufgabe, 14 Punkte. Programmieren Sie das Verfahren RadixSort zum Sortieren einer verketteten Liste von ganzzahligen int-Werten mit 32 Bits in linearer Zeit. Zerlegen Sie dazu die intWerte x in vier Ziffern“ zu je 8 Bits. Diese Ziffern liegen im Bereich 0–255 und ” können durch Bitverschiebungen und Bitmasken aus x bestimmt werden: x & 0xff, (x>>>8)&0xff, (x>>>16)&0xff, und (x>>>24). Zum stabilen Sortieren nach einer Ziffer verwenden wir nicht Sortieren durch Zählen (Countingsort), sondern folgende Methode, die für verkettete Listen L mit n Elementen besser geeignet ist: Wir erstellen ein Feld von 256 leeren Listen T0 , . . . , T255 ; dann durchlaufen wir L in O(n) Zeit und verteilen die Elemente auf diese Listen, indem wir jedes Element x am Ende der Teilliste Ti anhängen, wenn die rechteste Ziffer von x den Wert i hat. Danach verketten wir die Listen T0 , . . . , T255 zu einer neuen Gesamtliste L0 und wiederholen das Verfahren für die zweite Ziffer von rechts, usw. Am Ende, bei der linkesten Ziffer, müssen wir zuerst die Fächer T128 , T129 , . . . , T255 zusammenhängen und anschließend die Fächer T0 , T1 , . . . , T127 , damit das Vorzeichen richtig berücksichtigt wird und die negativen Zahlen vor den positiven kommen. (a) Schreiben Sie ein Programm für diesen Sortieralgorithmus. Benützen Sie für die Teillisten Ihre Lösung von Aufgabe 53. Zum Einlesen ganzer Zahlen von der Konsole oder aus einer Datei können Sie die Klasse IntReader1 verwenden. (b) Zusatzfrage, 0 Punkte. Das Sortieren nach jeder Ziffer muss eigenlich stabil sein. Kann man trotzdem für die Teillisten auch Stapel statt Schlangen verwenden? 1 http://www.inf.fu-berlin.de/lehre/SS16/ALP2/IntReader.java 17 Algorithmen und Programmierung 2, SS 2016 — 14. Übungsblatt Freiwillige Abgabe bis Freitag, 15. Juli 2016, 12:00 Uhr 63. Sortierte Reihenfolge, Programmieraufgabe, 5 Punkte Schreiben Sie ein Java-Programm istSortiert zum Testen, ob ein binärer Baum ein Suchbaum ist: Für jeden Knoten k mit Schlüssel (wert) x muss gelten: Alle Schlüssel im linken Teilbaum von k sind kleiner als x; alle Schlüssel im rechten Teilbaum von k sind größer als x. Verwenden Sie bei dieser und bei den beiden folgenden Aufgaben die Konvention von Aufgabe 61. Ihr Programm soll nur lineare Zeit brauchen. Sie dürfen zusätzliche Datenstrukturen zu Hilfe nehmen. 5 Zusatzpunkte gibt es für ein Programm, das ohne zusätzliche Datenstrukturen auskommt. Rekursion dürfen Sie verwenden. 64. Haldenordnung, Programmieraufgabe, 5 Punkte Schreiben Sie ein Java-Programm istHalde zum Testen, ob ein binärer Baum die Haldeneigenschaft erfüllt: Die Kinder jedes Knotens k müssen einen größeren Wert haben als k. 65. Niveau-Reihenfolge, Programmieraufgabe, 0 Punkte Schreiben Sie ein Java-Programm, das die Knoten eines Binärbaumes in NiveauReihenfolge ausgibt, das heißt, nach Tiefe sortiert: Zuerst kommt die Wurzel, dann die Kinder der Wurzel von links nach rechts, usw. Ihr Programm soll nur lineare Zeit brauchen. Sie dürfen zusätzliche Datenstrukturen zu Hilfe nehmen. (Die Tiefe eines Knotens ist der Abstand zur Wurzel. Die Höhe eines Baumes ist die größte Tiefe eines Knotens.) 66. Supermarktkassen, Programmieraufgabe, 2 × 5 Punkte Modifizieren Sie das Simulationsprogramm aus der Vorlesung, sodass es folgende Varianten behandelt: (a) Es gibt nur eine gemeinsame Schlange für alle Kassen. (b) Es gibt eine getrennte Schlange für jede Kasse. Neu ankommende Kunden stellen sich bei der kürzesten Schlange an. Wenn mehrere Schlangen die gleiche Länge haben, dann wird die erste“ Schlange (mit der kleinsten Nummer) gewählt. ” 67. (0 Punkte) Stellen Sie die Hierarchie aller Klassen und Schnittstellen des Programms Simulation dar. 68. Verständnisfragen, 10 Punkte (a) Wann muss man einen eigenen Konstruktor für eine Unterklasse schreiben und kann nicht automatisch den Konstruktor für die Oberklasse übernehmen? (b) Welche Vorteile haben generische Klassen und generische Methoden. (c) Warum muss die main-Methode in Java als statische Methode definiert sein? (d) Warum kann es vorteilhaft sein, eine geschachtelte Klasse innerhalb einer anderen Klasse zu definieren, statt auf der äußersten Ebene (der Paketebene)? (e) Warum kann man die Klasse Ereignis im Programm Simulation.java nicht einfach ohne die Methode bearbeite definieren, statt Ereignis als abstrakte Klasse zu definieren? (f) Was ist der Unterschied zwischen dem Überschreiben und dem Überladen von Methoden? 18 Algorithmen und Programmierung 2, SS 2016 — Klausur Abgabe bis Montag, 18. Juli 2016, 16:00 Uhr 69. Typumwandlung und Konstruktoren in Java, 10 Punkte (a) (6 Punkte) Die Variablen i, f, d seien so deklariert: int i; float f; double d; Welche der folgenden drei Anweisungen sind korrekt? i = f * d; // 1. d = i * f; // 2. f = i * d; // 3. Erläutern Sie für jede gültige Anweisung genau, was passiert. Welche Größe wird wann in welchen Typ umgewandelt? Ergänzen Sie die ungültigen Anweisungen durch explizite Typumwandlungen so, dass sie gültig werden. (Eventuell gibt es mehrere korrekte Möglichkeiten.) (b) (4 Punkte) Welche der folgenden Aussagen über Konstruktoren sind richtig? Geben Sie im positiven Fall ein Beispiel von Klassen mit einem Aufruf eines solchen Konstruktors an. i. Eine Unterklasse erbt automatisch alle Konstruktoren der Oberklasse. ii. In einem Konstruktor kann man einen anderen Konstruktor derselben Klasse aufrufen. 70. Binärbäume, 10 Punkte Ergänzen Sie die folgende Klassendefinition von Binärbaum in Java durch eine Methode, die die Höhe eines Binärbaums bestimmt. class Binärbaum <T> { T wert; Binärbaum <T> links, rechts; // Kinder ... } Die Höhe ist die Länge des längsten Weges von der Wurzel zu einem Blatt. Ein Baum mit einem einzigen Knoten hat also Höhe 0. Für den leeren Baum, der durch null dargestellt wird, legen wir fest, dass die Höhe −1 sein soll. 71. O-Notation, 10 Punkte (a) (4 Punkte) Gegeben sind zwei positive Funktionen S, T : N → R>0 . Beweisen Sie: Aus S(n) = Ω(f (n)) und T (n) = Ω(g(n)) folgt, dass S(n)+T (n) = Ω(f (n)+ g(n)) ist. (b) (3 Punkte) Geben Sie einen möglichst einfachen Ausdruck der Form Θ(f (n)) für folgenden Ausdruck an. Eine Begründung ist nicht erforderlich. a(n) = 3n3 + 25n2 + 100 · 2n + 65 (c) (3 Punkte) Geben Sie einen möglichst einfachen Ausdruck der Form Θ(f (n)) für folgenden Ausdruck an. Eine Begründung ist nicht erforderlich. b(n) = 3n3 + 10n + 66 log2 n + 16 19 72. Verschmelzen, 10 Punkte Das folgende Python-Programm verschmelzt zwei sortierte Listen von Zahlen a und b zu einer gemeinsamen sortierten Liste c. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 def verschmelze(a,b): """ Voraussetzung 1: a und b sind Listen von Zahlen Voraussetzung 2: a ist sortiert: a[i] <= a[i+1] für 0<=i<len(a)-1 Voraussetzung 3: b ist sortiert: b[i] <= b[i+1] für 0<=i<len(b)-1 """ m = len(a) n = len(b) c = [0] * (m+n) # erzeugt eine Liste der Länge m+n i = j = k = 0 while k<m+n: assert(...) _____________________________ (1.) if i==m or (j<n and a[i]>b[j]): assert(...) _________________________ (2.) c[k] = b[j] j = j+1 k = k+1 else: assert(...) _________________________ (3.) c[k] = a[i] i = i+1 k = k+1 assert(...) _____________________________ (4.) return c (a) (6 Punkte) Fügen Sie an den nummerierten Stellen aussagekräftige Invarianten ein, aus denen die Korrektheit des Programmes hervorgeht. (Schreiben Sie die Invarianten nicht auf dieses Blatt, sondern in die Klausurblätter.) Sie können dabei mathematische Notation mit Python-Syntax mischen. Zum Beispiel könnte die erste Invariante ungefähr so beginnen: (1.) c[:k] enthält die gleichen Elemente wie a[:i] + b[:j] ∧ m = len(a) ∧ isSorted(c[:k]) ∧ k ≥ 0 ∧ . . . wobei das Prädikat isSorted so definiert ist: isSorted(L) ⇐⇒ ∀u : (0 ≤ u < len(L) − 1) ⇒ L[u] ≤ L[u + 1] Die Gültigkeit der Invarianten brauchen Sie nicht zu beweisen. Bedingungen, die an mehreren Stellen gelten, sollten Sie zweckmäßigerweise zusammenfassen und mit einer Abkürzung bezeichnen. (b) (4 Punkte) Begründen Sie unter Zuhilfenahme Ihrer Invarianten, dass in den Listenzugriffen wie a[i] oder c[k] keine Listenindizes außerhalb der Listengrenzen auftreten. (c) (3 Zusatzpunkte) Beweisen Sie, dass die Funktion terminiert. 20