Programmiergrundkurs Aufgaben und Anleitung Lucas Mann 13.01.2016 1 Übersetzung in Python Als erstes machen wir uns damit vertraut, wie man in Python Algorithmen schreiben kann. Einen Algorithmus kannst du folgendermaßen definieren: def mein_algorithmus(a, b): Schritt1 Schritt2 ... Dabei ist mein_algorithmus der Name des Algorithmus und a und b sind die Eingabewerte (auch Parameter genannt). Natürlich kann dein Algorithmus auch mehr (oder weniger) Eingabewerte haben. Die folgende Tabelle zeigt, wie die einzelnen Schritte eines Algorithmus aus der Umgangssprache in Python übersetzt werden: Umgangssprache Python Eingabe: a, b. Kein extra Schritt, da in der Algorithmusdefinition enthalten (s.o.) Ausgabe: r . Wenn a > 2: Schritt1 ... Sonst, wenn a < 0: ... Sonst: ... Wiederhole solange, wie n > 0: Schritt1 ... Für k im Bereich(1, n): ... Setze a = 0 Erhöhe x um n Wende Algorithmus “alg” auf die Werte n und 3 an. Speichere das Ergebnis des Algorithmus “alg”, angewendet auf a, in r . Ausgabe: Ergebnis des Algorithmus “abc”. return r if a > 2: Schritt1 ... elif a < 0: ... else: ... while n > 0: Schritt1 ... for k in range(1, n + 1): ... a = 0 x = x + n oder x += n alg(n, 3) r = alg(a) return abc() In Python gibt es verschiedene Arten von Werten, zum Beispiel Zahlen, Wahrheitswerte und Texte. Die nächste Tabelle zeigt, wie du verschiedene Arten von Werten im Code eintippst: 1 Werttyp Ganze Zahl Reelle Zahl Wahrheitswert Text Beispielwerte 0, 5, −14 3,1415 “Wahr”, “Falsch” “Hello World” In Python 0, 5, -14 3.1415 True, False "Hello World" Wenn man zwei Werte vergleichen möchte (zum Beispiel in einem “Wenn . . . :”), hat man die folgenden Relationen zur Verfügung: Mathematisch Python = 6= < > ≤ ≥ == != < > <= >= Außerdem gibt es die logischen Verknüpfungen not, and und or. Zum Beispiel überprüft if (a > 0 and b < 0) or (a < 0 and b > 0): ob a und b verschiedene Vorzeichen haben. Es folgt ein Beispiel für einen Algorithmus in Umgangsprache und seine Übersetzung in Python: 1. Eingabe: Eine ganze Zahl n. 2. Wenn n < 0: def gauss_summe(n): if n < 0: return "Ungueltiges n" r = 0 while n > 0: r = r + n n = n - 1 return r 2.1. Ausgabe: „Ungültiges n“. 3. Setze r = 0. 4. Wiederhole die folgenden Schritte solange, wie n > 0: 4.1. Erhöhe r um n. 4.2. Verringere n um 1. 5. Ausgabe: r . Zur Vorbereitung für die folgenden Aufgaben implementiere obigen Algorithmus wie folgt in Python: 1. Tippe obigen Code in eine Textdatei und gib dieser Datei den Namen algorithmen.py. 2. Öffne ein Terminal, gehe dort mittels cd Pfad zu dem Ordner, in dem sich die Codedatei befindet. 3. Tippe python3 ein, um Python zu starten. 4. Tippe from algorithmen import * ein, um deine Codedatei zu laden. 5. Nun kannst du alle Algorithmen in der Codedatei benutzen. Tippe zum Beispiel gauss_summe(4) ein; es erscheint das Ergebnis 10. Die folgenden Aufgaben verlangen von dir, die im Kurs behandelten Algorithmen in Python zu übersetzen. Aufgabe 1. Implementiere den Algorithmus teilt(a, b), welcher zurückgibt, ob die Zahl b durch die Zahl a teilbar ist. Benutze als Vorbild den im letztwöchigen Kurs erarbeiteten Algorithmus. Aufgabe 2. Implementiere den Algorithmus ist_prim(n), der zurückgibt, ob die Zahl n eine Primzahl ist. Aufgabe 3. Implementiere den Algorithmus fakultaet(n), welcher n! ausrechnet. Aufgabe 4. Implementiere alle Algorithmen, die in Hausaufgabenserie 12 vorkamen. 2 2 Neue Algorithmen Hier sind ein paar weitere Aufgaben, für die du dir nun deine eigenen Algorithmen überlegen musst. Aufgabe 5. Schreibe einen Algorithmus binomial(n, k), welcher den Binomialkoeffizienten ¡n ¢ k berechnet. Zur Erinnerung: Es gilt à ! n n(n − 1)(n − 2) . . . (n − k + 1) n! = . = k k! (n − k)! k! Welche der beiden Formeln lässt sich leichter implementieren? Und welche erfordert weniger Rechenaufwand? Aufgabe 6. Schreibe einen Algorithmus, der die n-te Fibonacci-Zahl F n ausrechnet. Benutze dabei die folgenden drei Ansätze: a) Benutze die rekursive Vorschrift F 0 = 0, F 1 = 1, F n = F n−1 + F n−2 für n ≥ 2 in einer Schleife. b) Benutze die explizite Darstellung Fn = wobei φ = φn − (1 − φ)n , p 5 p 1+ 5 2 der Goldene Schnitt ist. p Tipp: Zur Berechnung von 5 benutze den Python-Code 5**.5. c) Die Vorschrift F n = F n−1 + F n−2 kannst du auch direkt implementieren, indem du deinen Algorithmus sich selbst aufrufen lässt (das nennt man Rekursion). Du musst dann allerdings aufpassen, dass der Algorithmus irgendwann beendet wird. Wie? 3 Listen Listen sind ein sehr wichtiger Datentyp in Python. Wie der Name sagt, ist eine Liste eine endliche geordnete Aneinanderreihung von irgendwelchen Werten (zum Beispiel die Liste aller Primzahlen von 1 bis 100). Listen werden mit eckigen Klammern geschrieben, also zum Beispiel l1 = [1, 1, 2, 3, 5, 8] l2 = ["Hallo", True, 1, -3.2] l3 = [] # leere Liste Die folgende Tabelle zeigt ein paar wichtige Operationen mit Listen: Umgangssprache Setze den 5-ten Eintrag der Liste l auf „Wahr“ Setze den a-ten Eintrag der Liste l auf den Wert in k Lies den 3-ten Eintrag der Liste l und speichere ihn in r Hänge den Text „Python“ hinten an die Liste l an Erzeuge eine Liste mit 20 True-Einträgen Python l[5] = True l[a] = k r = l[3] l.append("Python") l = [True] * 20 Es ist dabei wichtig zu wissen, dass die Nummern der Einträge immer bei 0 anfangen zu zählen. In obiger Liste l2 ist also l2[1] = True und l2[0] = "Hallo". 3 Aufgabe 7. Schreibe einen Algorithmus primzahlen(n), der die Liste aller Primzahlen von 1 bis n ausgibt, indem du den bereits implementierten Algorithmus ist_prim(n) benutzt. Teste die Geschwindigkeit deines Algorithmus für große n und schätze seine Effizienz ein. Aufgabe 8. Die Collatz-Folge zu einem Startwert n 0 (natürliche Zahl) wird wie folgt schrittweise berechnet: Sei n die aktuelle Zahl (am Anfang also n = n 0 ). In jedem Schritt tue folgendes: 1. Wenn n gerade ist, nimm als nächste Zahl n2 . 2. Wenn n ungerade ist, nimm als nächste Zahl 3n + 1. Es ist eine unbewiesene Vermutung, dass diese Folge immer irgendwann bei 1 landet, egal mit welchem n 0 man beginnt. Schreibe einen Algorithmus collatz(n0), der die Collatz-Folge mit Startwert n0 berechnet, bis irgendwann 1 herauskommt, und der die Folge von n0 bis 1 als Liste ausgibt. 4 Ein richtiges Programm Nachdem wir nun viele interessante Algorithmen geschrieben haben, wollen wir abschließend einen kleinen Einblick darin bekommen, wie ein richtiges Programm in Python aussieht. Wenn du Code direkt beim Laden des Programm ausführen willst (statt nur eine Sammlung von Algorithmen zu haben), dann kannst du diesen Code außerhalb aller Algorithmen (am besten darunter) schreiben, also ohne Einrückung. Damit das Programm mit dem Nutzer interagieren kann, gibt es zwei wichtige Befehle: 1. Mit print(wert) wird der angegebene Wert ausgegeben. Möchtest du einen Text ausgeben, schreibe ihn in Anführungszeichen (wie bereits weiter oben erklärt), also zum Beispiel print("Hallo"). Wenn du Werte in den Text einfügen willst, benutze format und geschweifte Klammern, zum Beispiel: r = 5 + 5 print("Anna ist {} und Bernd ist {} Jahre alt".format(r, 14)) Dadurch werden die an format übergebenen Werte anstelle der geschweiften Klammern ausgegeben (in der gleichen Reihenfolge). Natürlich kannst du statt direkten Werten (wie 10 und 14) auch die in Variablen (Speicherplätzen) gespeicherten Werte angeben. Das geht nicht direkt im Text! 2. Mit input(text) wird der Nutzer mittels der Nachricht text aufgefordert, etwas einzugeben. Das Ergebnis dieser Eingabe wird dann von input zurückgegeben und kann direkt in eine Variable gespeichert werden. Der Typ des Wertes ist dann aber automatisch „Text“; um diesen Text in eine Zahl zu konvertieren, kannst du int(wert) benutzen. Hier ein Beispiel für ein Programm, das den Nutzer auffordert, ein n einzugeben und dann n! ausgibt: print("Ich kann n! berechnen!") n = int(input("Bitte gib n ein: ")) r = fakultaet(n) print("Das Ergebnis ist {}".format(r)) Aufgabe 9. Wähle einen deiner Algorithmen aus und schreibe ein Python-Programm, welches diesen Algorithmus ausführt. Das heißt es sagt dem Nutzer was es tut, fragt dann die Eingabewerte ab und gibt schließlich das Ergebnis aus. 4 Der eleganteste Weg, das Programm auszuführen, ist den Befehl python3 datei.py einzutippen (also ohne das -i). Aufgabe 10. Schreibe das Spiel „Zahlen raten“: Der Computer wählt sich zu Beginn eine zufällige Zahl zwischen 1 und 1000. In jedem Zug darf der Spieler die Zahl raten und der Computer sagt ihm, ob seine Eingabe größer, kleiner oder gleich der zufälligen Zahl ist. Wenn der Spieler richtig rät, gibt der Computer die Anzahl der benötigten Versuche aus. Hinweis: Tue folgendes, um eine zufällige Zahl zwischen 1 und 1000 zu „berechnen“: Schreibe ganz oben in dein Programm die (uneingerückte!) Zeile import random, um die Zufallsalgorithmen in dein Programm zu laden. Um nun eine zufällige Zahl zu berechnen, benutze random.randint(1, 1000), also in etwa import random ... zahl = random.randint(1, 1000) ... 5 Zusatz Aufgabe 11. Gesucht sind die Lösungen zu folgendem Kryptogramm: EINS +EINS = ZW EI. Dabei steht jeder Buchstabe für eine Ziffer. Der Einfachheit halber erlauben wir, dass zwei verschiedene Buchstaben die gleiche Ziffer haben (Zusatz: Schaffst du es auch sonst?). Schreibe einen Algorithmus kryptogramm(), welcher alle Lösungen des Kryptogramms berechnet und in einer Liste zurückgibt. Benutze dafür die „brute force“-Methode (= rohe Gewalt), d.h. gehe alle Möglichkeiten für die Belegungen der 6 Buchstaben durch und überprüfe jeweils, ob die Gleichung erfüllt ist. Tipp: Du musst wahrscheinlich for-Schleifen ineinander verschachteln. Dein Algorithmus könnte also folgendermaßen anfangen: def kryptogramm(): for e in range(0, 10): for i in range(0, 10): ... Aufgabe 12. In Aufgabe 7 hast du einen Algorithmus programmiert, der dir die Primzahlen von 1 bis n ausgibt (für ein gegebenes n). Allerdings ist dieser Algorithmus sehr ineffizient und läuft für großes n nicht mehr in akzeptabler Zeit. Ein wesentlich schnellerer Algorithmus, der das gleiche Ergebnis erzeugt, ist das Sieb des Eratosthenes. Die Idee ist folgende: Zu Beginn wird eine Liste ` mit n Einträgen angelegt, wobei erstmal alle Einträge True sind. Nun werden die folgenden Schritte wiederholt: Suche den kleinsten Index k ≥ 2, sodass der k-te Eintrag von ` den Wert True hat. Dann ist k eine Primzahl, speichere k also in eine separate Primzahlliste. Nun gehe alle Vielfachen von k durch und setze die entsprechenden Einträge in ` auf False. a) Überlege dir, warum dieser Algorithmus funktioniert. Vergleiche obige Beschreibung mit der Erklärung auf Wikipedia; ist das der gleiche Algorithmus? b) Implementiere den Algorithmus eratosthenes(n) nach obigem Vorbild. Vergleiche die Geschwindigkeit dieses Algorithmus mit dem bereits implementierten primzahlen(n). 5 Tipp 1: Um eine Liste mit n Einträgen True zu erzeugen, kannst du l = [True] * n benutzen (siehe oben). Tipp 2: Um alle Vielfachen einer Zahl durchzugehen, kannst du eine for-Schleife benutzen. Man kann nämlich range sogar drei Parameter übergeben, wobei der dritte Parameter dann die Schrittweite ist. Zum Beispiel: for k in range(a, b, c): Eine solche Schleife wird für alle k der Form a, a + c, a + 2c, a + 3c, . . . wiederholt, solange diese Zahlen kleiner als b sind. Aufgabe 13. Das Ziel dieser Aufgabe ist es, die bekannte Kreiszahl π zu berechnen. a) Mithilfe elementarer Analysis kann man zeigen: µ ¶ 1 1 1 1 1 π = 4· 1− + − + − ±... 3 5 7 9 11 Schreibe einen Algorithmus pi(n), welcher die ersten n Additionen/Subtraktionen dieser Formel ausrechnet und somit eine Näherung für π bestimmt. Teste die Formel für große n – wie gut ist die Näherung? b) Ein wesentlich besserer, aber leider auch komplizierter Weg zur Berechnung von π ist folgendermaßen gegeben: Als erstes berechnet man zwei Zahlenfolgen (a n ) und (b n ) durch folgende rekursive Vorschrift: p a 0 = 1, b 0 = 0.5, p a k−1 + b k−1 ak = , b k = a k−1 · b k−1 , für k ≥ 1. 2 Zu gegebenem n berechnen wir nun s n = 20 (a 0 − b 0 )2 + 21 (a 1 − b 1 )2 + 22 (a 2 − b 2 )2 + 23 (a 3 − b 3 )2 + · · · + 2n (a n − b n )2 . Dann ist eine gute Näherung für π gegeben durch π≈ (a n + b n )2 , 1 − sn wobei die Näherung besser wird, je größer man n wählt. Implementiere den Algorithmus pi2(n), welcher eine Näherung für π nach diesem Verfahren berechnet. Berechne pi2(4) und vergleiche die Genauigkeit (und Geschwindigkeit) dieser Rechnung mit pi(10000). Was fällt auf? Tipp: Um die Wurzel aus einer Zahl x zu berechnen, kannst du x**.5 benutzen. Bemerkung: Der zweite Algorithmus ist so schnell, dass man sehr leicht an die Rechengenauigkeit von Python herankommt. In der Tat lässt sich aufgrund dieser Rechengenauigkeit kein genauerer Wert als pi2(4) bestimmen. Natürlich gibt es Methoden, die Rechengenauigkeit zu erhöhen. Bei Interesse bitte nachfragen. Aufgabe 14. Schreibe einen kleinen Taschenrechner, welcher die vier Grundrechenarten sowie Potenzen und Binomialkoeffizienten berechnen kann. Das Programm nimmt als Eingaben zwei Zahlen a und b und eine Operation und gibt das Ergebnis der entsprechenden Rechnung aus. Die Operation wird als Text angegeben und kann +, -, *, /, ^ oder b sein. Tipp: Benutze input(text), um die beiden Zahlen und die Operation vom Nutzer abzufragen. Bei den Zahlen musst du noch int(...) darumschreiben, um den eingegebenen Text in eine Zahl umzuwandeln (wie zuvor), bei der Operation nicht (denn diese soll ja als Text behandelt werden). 6