Programmierkurs

Werbung
Programmierkurs
Aufgaben und Anleitung
Lucas Mann
19.04.2017
1 Wiederholung
Wir wiederholen kurz, wie man in Python Algorithmen schreiben und ausführen kann.
1.1 Programmstruktur
Ein Algorithmus (oft auch als Funktion bezeichnet) wird folgendermaßen definiert:
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 grundlegenden 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”.
1
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()
1.2 Konstanten
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:
Werttyp
Ganze Zahl
Reelle Zahl
Wahrheitswert
Text
Zeichen
Liste
Beispielwerte
0, 5, −14
3,1415
“Wahr”, “Falsch”
“Hello World”
“d”
[ 1, 2, “Wahr”, 5 ]
In Python
0, 5, -14
3.1415
True, False
"Hello World"
’d’
[1, 2, True, 5]
1.3 Operatoren
Operatoren berechnen einen neuen Wert aus einem oder zwei vorhandenen Werten. Bei diesen
Werten kann es sich um Zahlen, Wahrheitswerte und Texte handeln, wobei abhängig vom Werttyp
unterschiedliche Operatoren möglich sind. Wir fassen im Folgenden einiger dieser Operatoren
zusammen:
Operator
+, −, ·, /
Ganzzahldivision
In Python
a+b, a-b, . . .
Modulo
Potenz
Minimum, Maximum
a % b
a ** b
min(a, b),
max(a, b)
Betrag
Gleich, ungleich
abs(a)
a == b, a != b
Zahlvergleiche
a < b, a >= b,
a // b
Beschreibung
Grundrechenarten
Wie Division, aber Nachkommastellen des
Ergebnisses werden abgeschnitten
Berechnet a mod b
Berechnet a b
Berechnet den kleineren bzw. größeren Wert
zweier Zahlen; kann auch auf eine Liste angewendet werden, z.B. min(l)
Berechnet |a|
Überprüft, ob a und b den gleichen Wert haben
Vergleicht die Größe zweier Zahlwerte
...
Und, oder, nicht
Listenzugehörigkeit
a and b,
a or b, not a
x in l
Verknüpft zwei Wahrheitswerte
Ist “Wahr”, wenn x in der Liste l ist
Zum Beispiel berechnet
if (a > 0 and b < 0) or (a < 0 and b > 0):
ob a und b verschiedene Vorzeichen haben; in diesem Fall bekommt obiger Ausdruck den Wert
“Wahr”, sonst “Falsch”.
1.4 Listen
Eine Liste ist eine endliche Folge von Werten, wobei die Werte innerhalb einer Liste auch verschiedene Typen haben können (in den meisten Fällen hat man aber eine Liste von lauter gleichen
Typen, zum Beispiel alles Zahlen).
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
2
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
Nimm die Unterliste von l1 der Indizes im Bereich von
a bis b − 1 und speichere sie in l2
Nimm die Unterliste von l1 der Indizes von a bis zum
Ende und speichere sie in l2
Hänge den Wert 3,1414 hinten an die Liste l an
Hänge die Listen l1 und l2 aneinander und speichere
das Ergebnis in l3
Sortiere die Liste l aufsteigend
Invertiere die Reihenfolge der Elemente in l
Speichere die Länge der Liste l in x
Python
l[5] = True
l[a] = k
r = l[3]
l2 = l1[a:b]
l2 = l1[a:]
l.append(3.1415)
l3 = l1 + l2
l.sort()
l.reverse()
x = len(l)
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", ferner ist l1[2:4] = [2, 3]
und l2[1:] = [True, 1, -3.2].
Beachte, dass Texte sehr ähnlich zu Listen sind: Ein Text ist im Wesentlichen eine Liste von Zeichen. Daher funktionieren einige der Operationen von Listen auch mit Texten. Ist zum Beispiel a
= "Hello World!", so ist a[4] = ’o’ und a[2:8] = "llo Wo".
1.5 Beispiel
Es folgt ein Beispiel für einen Algorithmus in Umgangsprache und seine Übersetzung in Python:
1. Eingabe: Eine ganze Zahl n.
2. Setze n = |n|.
3. Setze r = [] (eine leere Liste).
def number_to_digits(n):
n = abs(n)
r = []
while n > 0:
r.append(n % 10)
n = n // 10
return r
4. Wiederhole die folgenden Schritte solange, wie n > 0:
4.1. Hänge an r den Wert n mod
10 an.
4.2. Teile n durch 10 und schneide alles hinter dem Komma
ab.
5. Ausgabe: r .
Implementiere obigen Algorithmus wie folgt in Python:
1. Tippe obigen Code in eine Textdatei und gib dieser Datei den Namen digits.py.
2. Öffne ein Terminal, gehe dort mittels cd Pfad zu dem Ordner, in dem sich die Codedatei
befindet.
3. Tippe python3 -i digits.py ein, um Python zu starten und deine Codedatei zu laden.
4. Nun kannst du alle Algorithmen in der Codedatei benutzen. Tippe zum Beispiel number_to_digits(143)
ein; es erscheint das Ergebnis [3, 4, 1].
3
2 Spielereien mit Ziffern und Listen
Nachdem du das Beispiel aus dem letzten Kapitel implementiert hast, hast du einen Algorithmus
number_to_digits in der Datei digits.py, welcher zu einer ganzen Zahl die Liste ihrer
Ziffern bestimmt. Ausgehend von diesem Beispiel wollen wir den Umgang mit Listen noch ein
bisschen üben. Löse dafür die folgenden Aufgaben, indem du weitere Algorithmen zu der Datei
digits.py hinzufügst, die natürlich den Algorithmus number_to_digits verwenden dürfen.
Aufgabe 1. Implementiere die folgenden Algorithmen:
(a) num_digits(n): Gibt die Anzahl der Ziffern der ganzen Zahl n zurück.
(b) cross_sum(n): Berechnet die Quersumme der ganzen Zahl n. Benutze dafür zunächst
eine Schleife. Als zweite Variante kannst du die Operation sum(l) benutzen, welche die
Summe der Elemente einer Liste berechnet.
(c) even_digits(n) Gibt “Wahr” zurück, wenn die ganze Zahl n nur gerade Ziffern besitzt,
sonst ”Falsch“.
(d) is_palindrom(n) Bestimmt, ob die ganze Zahl n ein Palindrom ist, also ob sie sich nicht
ändert, wenn man die Reihenfolge ihrer Ziffern umkehrt (zum Beispiel ist 43834 ein Palindrom).
Aufgabe 2. Schreibe den Algorithmus digits_to_number. Dieser nimmt eine Liste von Ziffern
und berechnet die zugehörige ganze Zahl. Mit dessen Hilfe kannst du die folgenden Algorithmen
implementieren:
(a) invert_digits(n): Berechnet diejenige Zahl, die beim Umkehren der Reihenfolge der
Ziffern von n entsteht. Für n = 524 ergibt sich also das Ergebnis 425.
(b) sort_digits(n): Sortiert die Ziffern der Zahl n absteigend und gibt die resultierende
Zahl zurück. Für n = 85248 ergibt sich also 88542.
Aufgabe 3. Sei k eine positive ganze Zahl. Ein k-stelliger „Kaprikar-Schritt“ transformiert eine
k-stellige Zahl n wie folgt: Berechne die beiden Zahlen, die durch Sortieren der Ziffern von n in
aufsteigender bzw. absteigender Reihenfolge entstehen und subtrahiere sie voneinander. Dadurch
entsteht eine neue Zahl n 0 . Für vierstellige Zahlen gilt: Wiederholt man diesen Prozess oft genug,
so landet man immer bei der Zahl 6174, der sogenannten Kaprikar-Konstante.
(a) Implementiere einen Algorithmus kaprikar_step(n, num_digits), welcher eine Zahl
n und eine Anzahl an Ziffern nimmt und dann den Kaprikar-Schritt ausführt. Der Parameter
num_digits ist notwendig, da n auch weniger als die gewünschte Anzahl an Ziffern haben
darf – in diesem Fall sind die verbleibenden Ziffern mit 0 aufzufüllen. Zum Beispiel ist die
Zahl 728 im vierstelligen Modus als 0728 zu lesen.
Hinweis: Du musst als erstes den Algorithmus number_to_digits anpassen, sodass diese Funktion einen zweiten Parameter num_digits nimmt, der die Anzahl der zu berechnenden Ziffern angibt. Benenne den neuen Algorithmus am besten um, damit der alte verfügbar bleibt.
(b) Implementiere den Algorithmus kaprikar(n, num_digits), welcher den KaprikarSchritt auf die Startzahl n so lange anwendet, bis n sich nicht mehr ändert und den dann
erhaltenen Wert zurückgibt.
Teste für verschiedene Zahlen n, ob man tatsächlich immer irgendwann 6174 erhält.
4
3 Berechnung von π
Das Ziel dieses Abschnittes ist es, die bekannte Kreiszahl π zu berechnen. Dazu werden wir uns
verschiedene Methoden ansehen. Implementiere die folgenden Algorithmen in der Datei pi.py.
Aufgabe 4. Die sogenannte Monte-Carlo-Methode benutzt Wahrscheinlichkeitstheorie, um π auszurechnen. Betrachte einen Viertelkreis mit Radius 1 innerhalb eines Quadrates mit Seitenlänge 1.
Ist P ein zufälliger Punkt in dem Quadrat, dann ist die Wahrscheinlichkeit dafür, dass P im Viertelkreis liegt, genau π4 . Das Ziel ist es nun, diese Wahrscheinlichkeit empirisch zu berechnen, indem
man sehr viele Punkte zufällig generiert und dann die Anzahl der Punkte im Kreis durch die Anzahl
aller Punkte berechnet.
Schreibe einen Algorithmus pi_montecarlo(n), welcher die oben beschriebene Wahrscheinlichkeitsberechnung mit n zufälligen Punkten durchführt und die daraus gewonnene Näherung
für π zurückgibt.
Zum Generieren von Zufallszahlen kannst du das Modul random benutzen: Schreibe dazu an
den Anfang der Datei die Zeile from random import random. Nun kannst du mit random()
eine Zufallszahl zwischen 0 und 1 erzeugen.
Teste den Algorithmus für große n – Wie gut ist die Näherung?
Aufgabe 5. Mithilfe elementarer Analysis kann man zeigen:
µ
¶
1 1 1 1 1
π = 4· 1− + − + −
±...
3 5 7 9 11
Schreibe einen Algorithmus pi_analysis(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?
Aufgabe 6. 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:
a 0 = 1,
ak =
a k−1 + b k−1
,
2
p
0.5,
p
b k = a k−1 · b k−1 ,
b0 =
für k ≥ 1.
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 pi(n), welcher eine Näherung für π nach diesem Verfahren
berechnet. Berechne pi(4) und vergleiche die Genauigkeit (und Geschwindigkeit) dieser Rechnung mit pi_analysis(10000). Was fällt auf?
Tipp: Um die Wurzel aus einer Zahl x zu berechnen, kannst du x**.5 benutzen.
Bemerkung: Dieser 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 pi(4) bestimmen. Natürlich gibt es Methoden, die Rechengenauigkeit zu erhöhen. Bei
Interesse bitte nachfragen.
5
4 Verschlüsselung
In diesem Abschnitt beschäftigen wir uns mit einfachen Verschlüsselungsalgorithmen: der CäsarVerschlüsselung und der Vigenère-Verschlüsselung. Implementiere die folgenden Algorithmen in
einer Datei encryption.py.
Aufgabe 7. Die Verschlüsselungen ordnen den Zeichen des Alphabets üblicherweise Zahlwerte
zu: „A“ ist 0, „B“ ist 1, „C“ ist 2 und so weiter. Wir brauchen daher als erstes zwei Funktionen zum
Umwandeln eines Zeichens in den zugehörigen Zahlwert und umgekehrt. Dafür kann man die
folgenden Funktionen in Python verwenden:1
Python-Code
ord(c)
chr(n)
c = c.upper()
c.isalpha()
Bedeutung
Konvertiert das Zeichen c in den zugehörigen ASCII-Wert
Konvertiert den ASCII-Wert n in ein Zeichen
Konvertiert das Zeichen c in den zugehörigen Großbuchstaben
Überprüft, ob es sich bei c um einen Buchstaben handelt
Schreibe die folgenden Algorithmen:
(a) Der Algorithmus c2n(c) konvertiert den Buchstaben c in den zugehörigen Zahlwert. Beispielsweise sollen die folgenden Konvertierungen stattfinden:
„A“
0,
„a“
0,
„B“
1,
„b“
1,
„Z“
25,
„z“
25.
Falls c kein Buchstabe ist, spielt der Rückgabewert keine Rolle.
(b) Der Algorithmus n2c(n) konvertiert den Zahlwert n in den zugehörigen Großbuchstaben.
Zum Beispiel:
0
„A“,
1
„B“,
25
„Z“.
Aufgabe 8. Die Cäsar-Verschlüsselung funktioniert folgendermaßen: Der Schlüssel k ist ein Zahlwert zwischen 0 und 25. Bei der Verschlüsselung wird auf jeden Buchstaben des Klartextes der
Schlüssel k addiert (modulo 26) und der sich ergebende Buchstabe in den Kryptotext übernommen. Bei der Entschlüsselung wird der Schlüssel k zeichenweise subtrahiert.
(a) Schreibe den Algorithmus caesar_encrypt(text, key), welcher den Text text mit
dem Schlüssel key verschlüsselt und das Ergebnis zurückgibt. Enthält der Text Zeichen, die
keine Buchstaben sind, so werden diese Zeichen einfach ignoriert. Ferner gibt es keine Unterscheidung von Groß- und Kleinbuchstaben.
Hinweis: Python sieht die Umlaute ebenfalls als Buchstaben an (sinnvollerweise), doch unsere Konvertierung c2n funktioniert nicht mit Umlauten. Probiere daher am besten nur Texte ohne Umlaute.
(b) Schreibe den Algorithmus caesar_decrypt(text, key), welcher den Text text mit
dem Schlüssel key entschlüsselt.
(c) Wie sicher ist die Cäsar-Verschlüsselung? Lasse von einem Mitschüler einen Text verschlüsseln und versuche den verschlüsselten Text ohne Kenntnis des Schlüssels zu knacken.
Aufgabe 9. Die Vigenère-Verschlüsselung funktioniert folgendermaßen: Der Schlüssel k ist ein
Text. Dieser Text wird wiederholend unter den Klartext geschrieben. Ein Zeichen des Kryptotextes
ergibt sich als Summe (modulo 26) des Klartextzeichens und des darunter befindlichen Schlüsselzeichens.
1 Der ASCII-Wert eines Zeichens ist eine eindeutige Zahl, die jedem Zeichen zugeordnet ist. Der ASCII-Wert von „A“ ist
zum Beispiel 65, der von „B“ ist 66 und so weiter. Bei den Kleinbuchstaben ist es ähnlich: Der ASCII-Wert von „a“ ist
97, der von „b“ ist 98 und so weiter.
6
(a) Schreibe den Algorithmus vigenere_encrypt(text, key), welcher den Text text
mit dem Schlüssel key verschlüsselt. Der Schlüssel ist hierbei ein Text, welcher nur Buchstaben enthält.
(b) Schreibe den Algorithmus vigenere_decrypt(text, key), welcher den Text text
mit dem Schlüssel key entschlüsselt.
(c) Wie sicher ist die Vigenère-Verschlüsselung?
Zusatz: Schreibe einen Algorithmus vigenere_hack(text, key_len), welcher den
Text text entschlüsselt, indem er nur die Länge des Schlüssels verwendet, nicht den Schlüssel selbst. Diese Aufgabe ist deutlich schwieriger als die anderen Aufgaben und kann am
besten von dir zu Hause probiert werden.
5 Die Türme von Hanoi
„Die Türme von Hanoi“ ist der Name des folgenden Rätsels: Gegeben sind drei Lagerplätze für
Bausteine. Auf dem ersten Lagerplatz befinden sich zu Beginn n Bausteine, der Größe nach geordnet (der größte unten), die anderen beiden Lagerplätze sind leer. Die Aufgabe ist nun, alle Bausteine vom ersten auf den dritten Lagerplatz zu bewegen, indem man nacheinander einzelne Bausteine umstapelt. Dabei müssen jedoch zu jedem Zeitpunkt alle Bausteine auf allen Lagerplätzen der
Größe nach geordnet sein.
Schreibe die folgenden Algorithmen in der Datei hanoi.py.
Aufgabe 10. Schreibe einen Algorithmus hanoi(n), der das Hanoi-Problem zu gegebener Bausteinzahl n löst. Gehe dazu wie folgt vor.
Implementiere rekursiv den Algorithmus hanoi_move(x, y, z, k), welcher eine Lösung
zu folgendem Problem findet: Bewege die oberen k Steine von Platz x nach Platz z und benutze
dabei eventuell als Zwischenlager den Platz y. Das eigentliche Hanoi-Problem wird dann durch
den Aufruf hanoi_move(1, 2, 3, n) gelöst.
Tipp: Im Fall k = 1 ist hanoi_move(x, y, z, k) einfach. Verwende die Python-Funktion
print, um einen Text auszugeben; hierbei kannst du den Text-Operator % benutzen, um Werte
in den Text einzusetzen, zum Beispiel:
print("Bewege von {} nach {}"% (2, 3)).
Im Fall k > 2 kannst du das Problem mittels Rekursion lösen; in diesem Fall sind drei Aufrufe von
hanoi_move nötig.
6 Analytische Geometrie
In diesem Abschntit wollen wir Python ein wenig analytische Geometrie beibringen. Dazu betrachten wir die folgenden Arten von Objekten:
Punkte. Dargestellt durch eine Liste bestehend aus zwei Zahlen.
Geraden. Dargestellt durch eine Liste von einem Punkt und einem zweiten Punkt (die Richtung
der Gerade).
Schreibe die folgenden Algorithmen in der Datei geometry.py.
Aufgabe 11. Schreibe zunächst die Algorithmen point(x, y) und line(p, v), mit denen
man Punkte und Geraden erstellen kann.
7
Aufgabe 12. Schreibe Algorithmen add(p1, p2) und subtract(p1, p2), welche die Addition und Subtraktion von je zwei Punkten durchführen und das Ergebnis zurückgeben. Schreibe
analog mult(p, a), welches den Punkt p mit der Zahl a multipliziert und das Ergebnis zurückgibt. Schreibe schließlich lstinline{dot(p1, p2), welches das Skalarprodukt der beiden
gegebenen Punkte berechnet und zurückgibt.
Aufgabe 13. Implementiere die folgenden Algorithmen:
(a) midpoint(p1, p2): Berechnet den Mittelpunkt der gegebenen Punkte.
(b) focus(p1, p2, p3): Berechnet den Schwerpunkt des gegebenen Dreiecks.
(c) orthogonal(l1, l2): Überprüft, ob die beiden gegebenen Geraden senkrecht aufeinanderstehen.
Aufgabe 14. Implementiere den Algorithmus intersect(l1, l2), welcher den Schnittpunkt
der beiden gegebenen Geraden berechnet. Falls die Geraden keinen Schnittpunkt haben, wird
None zurückgegeben.
8
Herunterladen