hue05 December 14, 2016 HIER und NUR HIER Matrikelnummer und email eintragen; diese Zeile entfernen (s.u)! 1 Abgabehinweise Beachten Sie unbedingt diese Hinweise, sonst erhalten Sie keine Punkte aus dieser Abgabe! Für Details siehe z.B. Folien der nullten Zentralübung 1.1 Namen und Matrikelnummern Tragen Sie Ihre Matrikelnummern und E-Mail-Adressen zeilenweise in die Zelle oberhalb dieser Zelle ein. Achten Sie dabei streng auf die Formatierung, denn die Auswertung erfolgt automatisch. Entfernen Sie den schon in der Zelle stehenden Text (YOUR ANSWER HERE). Benutzen Sie keine Leerzeilen. Zwischen Matrikelnummer und E-Mail-Adresse muss genau ein Leerzeichen stehen; sonst keine Leerzeichen, Tabs, Spiegelstriche, oder ähnliches in einer Zeile. Wir empfehlen dringend die Benutzung der Uni-Paderborn E-Mail-Adressen! Format: MatrikelNummer E-Mail-Adresse z.B.: 123456 [email protected] 1.2 Abgabe mit Gruppenaccount Die Abgabe muss mit Ihrem Gruppenaccount erfolgen (gp1_16_. . . ), den Sie in der Präsenzübung erhalten haben! Abgaben, die über Ihren Einzelaccount erfolgen, werden ignoriert. 1.3 Abgabe: Submit Es reicht nicht, nur das Übungsblatt zu verändern. Sie müssen unter Assignments auf Submit clicken (oder entsprechend über die Kommandozeile). Für Details siehe Beschreibung Abgabeprozess. 1.4 Dateinamen Geben Sie Ihre Lösung in der vom Server erhaltenen Datei hue05.ipynb ab (nicht umbenennen, keine Kopie erstellen, keine anderen Dateien in das Verzeichnis legen!). Sonst kann eine Bewertung nicht stattfinden. 1 1.5 Struktur des Notebooks Fügen Sie keine Zellen hinzu und löschen Sie keine Zellen. Ändern Sie nicht den Typ einer Zelle. Geben Sie Lösungen nur in den Lösungszellen ab. Änderungen in den anderen Zellen werden nach Abgabe automatisch rückgängig gemacht. 1.6 Code-Zellen Entfernen Sie die Zeilen mit Inhalt “raise NotImplementedError()” aus den Zellen, in die Sie Ihre Lösungen schreiben. Ersetzen Sie diese Zeilen durch Ihre eigene Lösungen. 1.7 Nie input Benutzen Sie niemals die Funktion input(). Das verhindert die automatische Auswertung Ihrer Abgabe und führt zu 0 Punkten für das gesamte Blatt! 1.8 Kommentare Kommentieren Sie Code-Abgaben! Insbesondere muss jede Funktion (Klasse, Methode, . . . ) einen docstring haben! Bei fehlendem docstring werden automatisch Punkte abgezogen! 2 Abgabetermin: 4.12.2016 um 23:59 Uhr 3 Aufgabe 1: King-Kong 3.1 a) Implementieren Sie eine Funktion king_kong(x), welche für eine gegebene natürliche Zahl x - ‘King’ zurückgibt, falls x durch 3 teilbar ist oder die Ziffer 3 enthält - ‘Kong’ zurückgibt, falls x durch 7 teilbar ist oder die Ziffer 7 enthält - ‘KingKong’ zurückgibt, falls x die beiden oberen Bedingungen erfüllt - x zurückgibt, falls sie keine der oberen drei Bedingungen erfüllt. In [ ]: def king_kong(x): ### BEGIN SOLUTION """Gibt 'King' zurück, falls x durch 3 teilbar ist oder die Ziffer 3 en falls x durch 7 teilbar ist oder die Ziffer 7 enthält. Gibt 'KingKong' Falls keine der genannten Bedingungen erfüllt ist, wird x zurückgegeben Keyword arguments: x -- (int) die zahl, welche auf oben genannte Bedingungen überprüft wir Return: list -- 'King', 'Kong', 'KingKong' oder die Zahl selber """ r = x king_erfuellt = False if x % 3 == 0 or '3' in str(x): r = 'King' 2 king_erfuellt = True if x % 7 == 0 or '7' in str(x): if king_erfuellt == True: return 'KingKong' return 'Kong' return r ### END SOLUTION In [ ]: # Tests, die Ihre Code bestehen muss # Verändern Sie den Inhalt dieser Zelle nicht. assert(king_kong(1) == 1) assert(king_kong(2) == 2) assert(king_kong(3) == 'King') assert(king_kong(6) == 'King') assert(king_kong(7) == 'Kong') assert(king_kong(17) == 'Kong') assert(king_kong(28) == 'Kong') assert(king_kong(21) == 'KingKong') assert(king_kong(37) == 'KingKong') In [ ]: # Verändern Sie den Inhalt dieser Zelle nicht. ### HIDESTART # Teste Ausgaben für durch 3 Teilbare Zahlen: for i in range(1,30): if i % 3 == 0 and not (i % 7 == 0 or '7' in str(i)): assert(king_kong(i) == 'King') # Teste Ausgaben für durch 7 Teilbare Zahlen: for i in range(1,30): if i % 7 == 0 and not (i % 3 == 0 or '3' in str(i)): assert(king_kong(i) == 'Kong') # Teste Ausgaben für Zahlen die 3, 7 oder beide Ziffern enthalten: for i in range(1,30): if (i % 3 == 0 or '3' in str(i)) and (i % 7 == 0 or '7' in str(i)): assert(king_kong(i) == 'KingKong') # Teste Zahlen, für welche kein String zurückgegeben wird: for i in range(1,30): if not((i % 3 == 0 or '3' in str(i)) or (i % 7 == 0 or '7' in str(i))): assert(king_kong(i) == i) ### HIDEEND 3.2 b) Implementieren Sie eine Funktion king_kong_liste(n), welche für eine gegebene natürliche Zahl n eine Liste der Zahlen aus {1...n} zurückgibt, für welche die Funktion king_kong aus Teil a) eine Zahl (also weder 'King' noch 'Kong' noch 'KingKong') zurückgibt. 3 Hinweis: Achten Sie auf Start- und Endindizes. Mit einer List comprehension ist das ganz leicht. In [ ]: def king_kong_liste(n): ### BEGIN SOLUTION """Gibt für eine gegebene natürliche Zahl n eine Liste der Zahlen aus { für welche die Funktion king_kong aus Teil a) eine Zahl liefert. Keyword arguments: n -- (int) die maximale zahl, für welche king_kong aufgerufen werden so Return: list -- eine liste mit zahlen """ return [x for x in range(1, n+1) if type(king_kong(x)) == int] #alternativ: # return [x for x in range(1, n+1) if str(king_kong(x)).isnumeric()] ### END SOLUTION In [ ]: # Tests, die Ihre Code bestehen muss # Verändern Sie den Inhalt dieser Zelle nicht. assert(king_kong_liste(10) == [1, 2, 4, 5, 8, 10]) In [ ]: # Verändern Sie den Inhalt dieser Zelle nicht. ### HIDESTART assert(king_kong_liste(50) == [1, 2, 4, 5, 8, 10, 11, 16, 19, 20, 22, 25, 2 ### HIDEEND 4 Aufgabe 2: Binomialkoeffizienten 4.0.1 a) Das Pascalsche Dreieck ist eine grafische Darstellung der Binomialkoeffizienten nk , aus der sich eine einfache Berechnung dieser Koeffizienten ergibt. Dabei kann die Variable n als Zeilenindex und k als Spaltenindex der Einträge des Pascalschen Dreiecks interpretiert werden. Hinweis: https://de.wikipedia.org/wiki/Pascalsches_Dreieck Leiten Sie aus dem Pascalschen Dreieck ein rekursives Vorgehen ab, um Binominalkoeffizienten zu berechnen. Schreiben Sie eine Funktion namens pascal, welche zwei ganzzahlige Parameter übergeben bekommt und die entsprechenden Binomialkoeffizienten mit Hilfe des Pascalschen Dreiecks berechnet und zurückgibt. Implementieren Sie die Funktion pascal mittels Rekursion. In [ ]: def pascal(row, col): ### BEGIN SOLUTION """Compute elements of Pascals triangle Keyword arguments: row -- (int) Row nr. col -- (int) Column nr. 4 Return: int -- pascal coefficient """ if (col == 0 or col == row): return 1 else: return pascal(row-1,col-1) + pascal(row-1,col) ### END SOLUTION In [ ]: # Tests, die Ihre Code bestehen muss # Verändern Sie den Inhalt dieser Zelle nicht. assert(pascal(0,0)==1) assert(pascal(1,0)==1) assert(pascal(1,1)==1) assert(pascal(10,1)==10) assert(pascal(10,5)==252) In [ ]: # Verändern Sie den Inhalt dieser Zelle nicht. ### HIDESTART import math def nCr(n,k): return (math.factorial(n))/(math.factorial(k)*math.factorial(n-k)) for n in range(1,20): for k in range(1,n+1): assert(pascal(n,k) == nCr(n,k)) ### HIDEEND 4.0.2 b) Schreiben Sie eine Funktion pd_print, die die ersten n Reihen des pascalschen Dreiecks in folgenderm Format ausgibt: 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 In [ ]: def pd_print(n): ### BEGIN SOLUTION """Print elements of Pascal-triangle Keyword arguments: n -- (int) nr. of rows Return: None """ for i in range(0,n): print(" "*(n-i-1),end="") for j in range(0,i+1): print(pascal(i,j),end=" ") 5 print("\n",end="") ### END SOLUTION In [ ]: # Verändern Sie den Inhalt dieser Zelle nicht. ### HIDESTART pd_print(1) print() pd_print(2) print() pd_print(3) print() pd_print(4) print() pd_print(5) ### HIDEEND 5 Aufgabe 3: Folgen implementieren 5.0.1 a) Schreiben Sie eine Funktion berechne_folge(n), welche für eine gegebene natürliche Zahl n eine Liste mit allen Elemenenten der wie folgt definierten Folge ausgibt. Die Elemente der Folge berechnen sich folgendermaßen: 1. 2. 3. 4. Beginne mit einer natürlichen Zahl n > 0. Ist n gerade, so nimm als nächstes n/2. Ist n ungerade, so nimm als nächstes 3n + 1. Wiederhole die Vorgehensweise mit der erhaltenen Zahl (als neues n). Die Folge endet, sobald eine 1 zur Folge hinzugefügt wurde. In [ ]: def berechne_folge(n): ### BEGIN SOLUTION """Berechnet die Elemente der Collatz-Folge für den übergebenen Startpa Keyword arguments: n -- (int) Startparameter Return: list -- Liste mit den Folgenelementen """ L = [] while(True): L.append(n) if n == 1: return L if n % 2 == 0: 6 n = n//2 else: n = 3*n+1 ### END SOLUTION In [ ]: # Tests, die Ihre Code bestehen muss # Verändern Sie den Inhalt dieser Zelle nicht. assert(berechne_folge(19) == [19, 58, 29, 88, 44, 22, 11, 34, 17, 52, 26, 1 assert(berechne_folge(15) == [15, 46, 23, 70, 35, 106, 53, 160, 80, 40, 20, ### HIDESTART assert(berechne_folge(7) == [7, 22, 11, 34, 17, 52, 26, 13, 40, 20, 10, 5, assert(berechne_folge(6) == [6, 3, 10, 5, 16, 8, 4, 2, 1]) assert(berechne_folge(5) == [5, 16, 8, 4, 2, 1]) ### HIDEEND 5.0.2 b) Begründen Sie, warum Sie in Ihrer Implementierung eine while- oder eine for-Schleife benutzt haben. • While-Schleife: Anzahl der Elemente der Folge ist nicht von vorneherein klar, daher bietet sich die While-Schleife an • for-Schleife: Macht hier keinen Sinn 6 Aufgabe 4 Schreiben Sie eine Funktion matrix_mult(X,Y), welche zwei gegebene Matrizen X = (xij ) ∈ Rl×m und Y = (yij ) ∈ Rm×n , (l, m, n ≥ 1) multipliziert und die Ergebnismatrix Z = (zik ) ∈ Rl×n zurückgibt. Dabei lässt sich der Eintrag von Z in Zeile i und Spalte k ermitteln durch Pm zik = j=1 xij · bjk . Falls die Dimensionen der übergebenen Matrizen nicht korrekt sind (d.h. falls die Spaltendimension von X ungleich der Zeilendimension von Y ist), so soll None zurückgegeben werden. Die Darstellung der Eingabematrizen läßt sich an den Tests ablesen. Sie dürfen annehmen, dass die Dimension der Matrizen immer größer als 0 ist. Hinweis: Python-Listen werden ab 0 indiziert. Die hier gegebene Definition der Matrix beginnt mit Index ab 1, aber lässt sich natürlich auch analog ab 0 auffassen. In [ ]: def matrix_mult(X,Y): ### BEGIN SOLUTION """Multipliziert die übergebenen Matizen und gibt die Ergebnismatrix zu unterschiedlichen Dimensionen nicht miteinander multipliziert werden kö Keyword arguments: X -- (int) erste Matrix Y -- (int) zweite Matrix Return: list -- None, oder eine Matrix, welche das Ergebnis X*Y enthält """ 7 # Teste Dimension: if len(X) != len(Y[0]): return None C = [[0 for col in range(len(Y[0]))] for row in range(len(X))] for i in range(len(X)): for j in range(len(Y[0])): for k in range(len(Y)): C[i][j] += X[i][k]*Y[k][j] return C ### END SOLUTION In [ ]: # Tests, die Ihre Code bestehen muss # Verändern Sie den Inhalt dieser Zelle nicht. X = [[12,7,3], [4,5,6], [7,8,9]] Y = [[5,8,1], [6,7,3], [4,5,9]] r = matrix_mult(X,Y) assert(r == [[114, 160, 60], [74, 97, 73], [119, 157, 112]]) # Falsche Dimension: X = [[12,7,3], [4,5,6], [7,8,9]] Y = [[5,8], [6,7], [4,5]] assert(matrix_mult(X,Y) == None) ### HIDESTART X = [[1,2,3], [4 ,5,3], [1 ,8,6]] Y = [[5,4,1], [6,2,3], [4,1,3]] r = matrix_mult(X,Y) assert(r == [[29, 11, 16], [62, 29, 28], [77, 26, 43]]) ### HIDEEND 7 Aufgabe 5 Der Dozent einer Programmiervorlesung nutzt ein tolles neues Tool, um die Lösungen seiner Studierenden möglichst komfortabel bewerten zu können. Leider erhält er von diesem Tool jedoch zwei getrennte Listen mit Matrikelnummern und Punkten, wie die folgenden: matrikel = [12345, 12531, 18034, 17824] punkte = [19, 13, 0, 20] Dabei bezieht sich der erste Eintrag in der Liste “Punkte” auf die erste Matrikelnummer, usw. Praktischer wäre es jedoch, ein dict zu haben, welches Matrikelnummern auf Puntke abbildet, z. B. so: matrikel_punkte = {12345: 19, 12531: 13, 18034: 0, 17824: 20} 8 7.0.1 a) Schreiben Sie eine Funktion namens verknuepfe(l1, l2), an die zwei Listen als Parameter übergeben werden und die die Verknüpfung dieser beiden Listen als Liste von Tuplen zurückgibt (so wie matrikel_punkte eine Verknüpfung der Listen matrikel und punkte ist). Hinweis: Verwenden Sie hierzu u. a. die Funktion zip(), https://docs.python.org/3/library/functions.html#zip In [2]: def verknuepfe(l1, l2): ### BEGIN SOLUTION """Formt zwei Listen in ein dict um, mit den Einträgen der ersten Liste den Einträgen der zweiten Liste als Werten (gemäß der Reihenfolge der V Ist eine der beiden Listen kürzer als die andere, werden die verbleiben anderen Liste ignoriert. Keyword arguments: l1 -- (list) Die erste Liste l2 -- (list) Die zweite Liste Return: dict -- Das so erzeugte dict """ return dict(list(zip(l1, l2))) ### END SOLUTION In [3]: # Tests, die Ihre Code bestehen muss # Verändern Sie den Inhalt dieser Zelle nicht. matrikel = [12345, 12531, 18034, 17824] punkte = [19, 13, 0, 20] matrikel_punkte = {12345: 19, 12531: 13, 18034: 0, 17824: 20} assert(matrikel_punkte == verknuepfe(matrikel,punkte)) ### HIDESTART assert({} == verknuepfe([], [])) ### HIDEEND 7.0.2 b) Leider stellt sich nach der Bewertung des dritten Übungsblattes heraus, dass das Bewertungs-Tool einen merkwürdigen Fehler hat: Befindet sich am Ende der Liste matrikel eine Folge von Matrikelnummern, deren zugehörige Studierende alle null Punkte erreicht haben, so tauchen diese in der Liste punkte nicht auf. Die liste punkte ist dann also kürzer als matrikel. Beispiel: matrikel = [12345, 12531, 18034, 17824, 12635] punkte = [6, 0, 3] anstelle von: punkte = [6, 0, 3, 0, 0] 9 Wie würde Ihre Funktion damit umgehen, dass die Längen der beiden Listen nicht übereinstimmen? Die betroffenen Matrikelnummern am Ende der Liste würden einfach nicht auftauchen. 7.1 c) Nach Veröffentlichung der Ergebnisse stellte sich heraus, dass einige Nachkorrekturen nötig waren. Der Dozent möchte die korrigierten Ergebnisse nun wieder in das Tool geben. Leider muss man dem Tool dazu wieder zwei Listen von der Form wie matrikel und punkte aus Teilaufgabe a) übergeben. Ihm liegen die korrigierten Ergebnisse jedoch nur in der Form wie matrikel_punkte vor. Schreiben Sie eine Funktion namens entknuepfe(d), welche ein solches dict übergeben bekommt und ein Paar (also 2-Tupel) der gewünschten Listen zurückgibt. Verwenden Sie hierzu abermals die Funktion zip(). Hinweis: Sie müssen hier keine Sonderfälle (leeres dict o.ä.) beachten. In [ ]: def entknuepfe(d): ### BEGIN SOLUTION """Erzeugt aus einem dict zwei Listen, die eine mit den Schlüsseln des den Werten des dicts und zwar so, dass das i-te Element der zweiten Lis ist, der in der ersten Liste an i-ter Stelle steht. Keyword arguments: d -- (dict) Das Eingabe-dict Return: (list,list) -- Die beiden von der Funktion erzeugten Listen """ entknuepft_als_liste_von_tuples = list(zip(*d.items())) l1 = list(entknuepft_als_liste_von_tuples[0]) l2 = list(entknuepft_als_liste_von_tuples[1]) return (l1, l2) ### END SOLUTION In [ ]: # Tests, die Ihre Code bestehen muss # Verändern Sie den Inhalt dieser Zelle nicht. matrikel_punkte = {12345: 19, 12531: 13, 18034: 0, 17824: 20} matrikel_punkte_entknuepft = entknuepfe(matrikel_punkte) matrikel = matrikel_punkte_entknuepft[0] punkte = matrikel_punkte_entknuepft[1] for i in range(len(matrikel)): assert(punkte[i] == matrikel_punkte[matrikel[i]]) 10 8 8.1 Aufgabe 6: Äquivalenz von Code a) Gegeben sind die folgenden beiden Codefragmente: L = [1, 2, 3, 4, 5] for i in range(len(L)): L[i] += 1 print(L) und L = [1, 2, 3, 4, 5] L = [x+1 for x in L] print(L) Finden Sie Situationen, wo diese beiden Code-Abschnitte nicht den gleichen Effekt haben. Hinweis: Was ist, wenn es andere Namen für die ursprünliche Liste gibt? Die folgenden beiden Codefragmente sind nicht äquivalent: L = [1, 2, 3, 4, 5] T = L for i in range(len(L)): L[i] += 1 print(T) # Gibt [2, 3, 4, 5, 6] aus L = [1, 2, 3, 4, 5] T = L L = [x+1 for x in L] print(T) # Gibt [1, 2, 3, 4, 5] aus 8.2 b) Gegeben ist die folgende Funktion schnitt(s1, s2), welche die Schnittmenge zweier Strings s1 und s2 berechnen soll. def schnitt(s1,s2): return [x for x in s1 if x in s2] Gegeben sind nun zwei beliebige Strings s1 und s2. Überlegen Sie sich, ob die Aufrufe schnitt(s1,s2) und schnitt(s2,s1) äquivalent sind. Begründen Sie Ihre Antwort. Die Aufrufe sind nicht äquivalent, wie das folgende Beispiel zeigt. def schnitt(s1,s2): return [x for x in s1 if x in s2] s1 = 'aaabbb' s2 = 'aabb' print(schnitt(s1,s2)) # Gibt ['a', 'a', 'a', 'b', 'b', 'b'] aus print(schnitt(s2,s1)) # Gibt ['a', 'a', 'b', 'b'] aus 11 Im ersten Aufruf wird für jedes a in s1 überprüft, ob sich a in s2 befindet. Da dies der Fall ist, enthält die resultierende Liste 3 mal 'a'. Im zweiten Aufruf wird lediglich für zwei 'a's überprüft, ob sich a in s1 befindet, wodurch die Ergebnisliste nur zwei mal 'a' enthält. 9 Aufgabe 7 Neben der Anweisung if BEDINGUNG: Block1 else: Block2 kennt Python einen Ausdruck: a if BEDINGUNG else b Sie können Details hier nachlesen: https://docs.python.org/3/reference/expressions.html#conditionalexpressions und https://www.python.org/dev/peps/pep-0308/ (Tutorial z.B. hier: http://pythoncentral.io/one-line-if-statement-in-python-ternary-conditional-operator/ ) 9.1 a) Setzen Sie Block1, Block2 und a, b zueinander in Bezug - was entspricht was? Block1 entspricht a, Block2 entspricht b. 9.2 b) Sind a und b Ausdrücke oder Blöcke? Es handelt sich hierbei um Ausdrücke (je nachdem, welcher Fall gilt, wird das Ergebnis des Gesamt-Ausdrucks einer dieser Ausdrücke). 10 Aufgabe 8 Angenommen, Sie haben zwei geschachtelte Schleifen. In der inneren Schleife führen Sie ein continue durch. Geben Sie ein Code-Beispiel an, aus dem ersichtlich wird, ob nur die innere oder auch die äußere Schleife beeinflusst wird. In [ ]: for i in range(2): print('i:', i) for j in range(100): if j >= 3: continue print('j:', j) print('i after:', i) 12 In [ ]: # Verändern Sie den Inhalt dieser Zelle nicht. ### HIDESTART assert(king_kong.__doc__ != None) assert(king_kong_liste.__doc__ != None) assert(pascal.__doc__ != None) assert(pd_print.__doc__ != None) assert(berechne_folge.__doc__ != None) assert(matrix_mult.__doc__ != None) assert(verknuepfe.__doc__ != None) # assert(verknuepfe_besser.__doc__ != None) assert(entknuepfe.__doc__ != None) ### HIDEEND 13