31
Inf K1/2 Sj 13/14 GZG FN W.Seyboldt 1 SFZ FN Sj. 13/14 Python 3 Rekursion

SFZ FN Sj. 13/14

Embed Size (px)

DESCRIPTION

SFZ FN Sj. 13/14. Python 3 Rekursion. Rekursion. Prinzip der Rekursion: In einer Funktion wird die Funktion wieder aufgegriffen. Das darf allerdings nicht ohne Ende geschehen. - PowerPoint PPT Presentation

Citation preview

Page 1: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt1

SFZ FN Sj. 13/14

Python 3 Rekursion

Page 2: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt2

Rekursion Prinzip der Rekursion: In einer Funktion wird die Funktion wieder

aufgegriffen. Das darf allerdings nicht ohne Ende geschehen. Wikipedia: Das Grundprinzip der Rekursion ist das Zurückführen einer

allgemeinen Aufgabe auf eine einfachere Aufgabe derselben Klasse. Das Grundprinzip der rekursiven Definition einer Funktion f ist: Der

Funktionswert f(n+1) ergibt sich durch Verknüpfung bereits berechneter Werte f(n), f(n-1), ... f(1) wird allerdings auf andere Art bestimmt.

Beispiel: Berechne n! Vorgehen: n! = n* (n-1)!. Das heißt, ich kann n! berechnen, wenn ich (n-1)! kenne.Es gilt 1!=1.

fak01.py: Schreibe ein Programm mit der Methode fakrek(n), die n! rekursiv berechnet. Sie ruft fakrek(n-1) auf und multipliziert das Ergebnis von fakrek(n-1) mit n. Dieses Ergebnis wird dann zurückgegeben. Ist n==1: return 1. Bestimme die ersten 100 Glieder von der Folge (n!)

Page 3: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt3

Rechenzeit

Rekursionen sind langsam und erfordern vom PC viel Aufwand.

Wir wollen nun die Rechenzeit bestimmen, die man benötigt, um alle Fakultäten rekursiv von 1 bis 500 zu bestimmen.

Ergänze fak01 und speichere es als fak01a.py um die benötigten Befehle.

from time import timestartzeit = time()… Rechnung …endzeit = time()print "Benötigte Rechenzeit = %8.6f s" %(endzeit-startzeit)

Page 4: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt4

Iteration statt Rekursion Wie kann man die Probleme von rekursiven Methoden lösen?

– Oft kann man Rekursionen durch Schleifen ersetzen– Oder man wendet das Prinzip der Rekursion mit Generatoren an

fak02.py: Schreibe ein Programm, das die Fakultät in Form einer Schleife berechnetNenne die Methode fakiter(n)

Das Programm soll die Methode weiterhin fakrek enthalten und die Rechenzeiten vergleichen.

Ergebnis: Die Rechenzeiten unterscheiden sich recht wenig. Das ist nicht immer so. Auf der nächsten Folie schauen wir uns ein komplexeres Problem an.

Page 5: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt5

Fibonacci-Folge, fib01,py Die Fibonacci-Folge: Die Fibonacci-Folge ist rekursiv

definiert: fib(n)=fIb(n-1)+fib(n-2) und fib(1)=fib(2)=1.– Die Fibonacci-Zahlen resultieren aus einem "künstlichen" Kaninchenproblem,

das die folgenden Bedingungen erfüllt: – Die Anfangspopulation wird von einem Kaninchenpaar gebildet – Ein neugeborenes Kaninchenpaar kann sich erst am Ende des ersten Monats

paaren und wirft am Ende des zweiten Monates ein weiteres Paar. – Ansonsten wirft jedes Kaninchenpaar jeweils ein weiteres Kaninchenpaar pro

Monat. – Sie sind unsterblich

Aufgabe fib01.py : Schreibe eine Methode fibr1(), die die Fibonacci-Folge rekursiv berechnet.Bestimme die ersten 30 Glieder derFibonacci-FolgeBestimme die Rechenzeit.

Page 6: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt6

Probleme der Rekursion

Warum dauert das Programm so lange? Wenn man z.B. fib(6) berechnet, sieht der

Aufrufbaum wie folgt aus: fib(2) wird also 5 mal aufgerufen.

Wie lässt sich dies verbessern?Ein iterative Lösung findet sich nicht (so leicht).

Was geht dann? Python bietet zwei Verfahren

Page 7: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt7

Verbesserung 1: fib02.py

Jedes Mal wenn wir ein Folgenglied berechnet haben, speichern wir den Wert in einer Liste namens fibMem ab.

Siehe fib02.pyAufgabe: Schreibe das Programm selbstNenne die entsprechende Methode fibr2()Messe die Rechenzeit für fibr2()

Ergebnis: Wenn wir fibr1(35) aufrufen, benötigt das Programm knapp 9 sWenn wir fibr2(999) aufrufen, erhalten wir die Antwort fast sofort

Aber Vorsicht: Auch bei dieser Variante, kann die Iterationstiefe überschritten werden.

Page 8: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt8

fibr2() - Vorschlag

fibMem=[0,1,1] # fibr2(0)..fibr2(2)

# Dies ist die globale Liste, die fib02 verwendet.

def fibr2(n):

# überprüfe zuerst, ob fib(n) schon berechnet wurde

if n<len(fibMem): return fibMem[n] # den noch nie berechneten Wert rekrusiv # berechen und abspeichern

fn=fibr2(n-1)+fibr2(n-2)

fibMem.append(fn)

return fn

Page 9: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt9

Generator (yield) Teil 1 Python bietet noch ein Verfahren an, das noch geschickter ist, wenn es auch

keinesfalls einfach zu verstehen ist. Ein Generator gen() ist eine Methode, die bei jedem Aufruf das nächste Element

einer virtuellen Sequenz mit yield statt return zurückgibt, die sozusagen einen Wert der Folge nach dem anderen produziert bzw. abwirft (englisch: yield, abwerfen, einbringen).Gestartet wird mit g=gen()Jeder Aufruf des Generators mit g.next() ergibt das nächste Folgenelement.

Definition einer Generatorsdefine fibo_generator():

setze Startwerteloop

berechne das nächste Folgenelementyield Folgenelement

end loop Anwenden eines Generators

fibo = fibo_generator()for n = 1 to 20: print fibo.next()

Lies http://www.python-kurs.eu/generatoren.php Erstelle das dort beschriebene Programm, speichere es in generator01.py

Page 10: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt10

Generator (yield) Teil 2

Der Generator kann dann in einer for-Schleife anstelle einer Liste verwendet werden.

Aufgabe generator02.py : Erstelle einen Generator, der einfach die Quadratzahlen liefert. Nachdem der Generator mit …next() einige Male aufgerufen wurde, soll ein neuer Generator erzeugt werden. Was beobachtest Du?

Aufgabe fib03.py: Erstelle ein Python-Programm, das mit einem Generator die Fibonacci-Zahlen zurückgibt.

Damit wird übrigens kein Überlauf mehr produziert, da kein (großer) Stack erzeugt

wird.

Page 11: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt11

Lösungsvorschlag fib03.py

def fib3gen(): # Generator (enthält yield()) a=1 b=1 while 1: yield a c=a+b a,b=b,c

def fib3r(n): fibo=fib3gen() for i in range(n+1): z=fibo.next() return z # jetzt wird der Geneator beendet

n=5000z= fib3r(n)print z

Page 12: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt12

Lösungsvorschlag fib04.py Da beim Abarbeiten einer Liste, jedesmal das nächste

Element erzeugt wird, kann statt der Liste auch die Methode stehen, die ein yield enthält. Siehe http://www.python-kurs.eu/generatoren.php „Generatoren und ihre Arbeitsweise“

def fib04gen(): a,b=1,1 while True: yield a a, b = b, a + bdef fib04(n): i=0 for z in fib04gen(): if i==n: return z i+=1

Page 13: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt13

Ein komplexer Generator

Lies http://www.python-kurs.eu/generatoren.php „Rekursiver Generator“

Erstelle perm01.py und teste den Code

Page 14: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt14

Pascalsches Dreieck

Aufgabe: Schreibe ein Programm, das das Pascalsche Dreieck anzeigt:

Die k. Zahl in der n. Zeile ist (n über k oder k aus n) Jede Zahl ist die Summe der beiden über ihr stehenden

Zahlen. (Falls es keine zwei darüber stehenden gibt ist die Zahl 1)

Es gilt auch: ist die Anzahl der Möglichkeiten k aus n Kugeln auszuwählen.

Rekursiv: PascalschesDreieck01.pyGenerator: PascalschesDreieck02.py

n

k

n n!

k k! n k !

Page 15: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt15

Die Kochsche Kurve: Kochkurve.py

Wikipdeia: Die Koch-Kurve ist ein von dem schwedischen Mathematiker Helge von Koch 1904 vorgestelltes Beispiel für eine überall stetige, aber nirgends differenzierbare Kurve.

Man kann die Kurve anschaulich mittels eines iterativen Prozesses konstruieren: Ersetze das mittlere Drittel einer jeden Teilstrecke durch die Spitze eines gleichseitigen Dreiecks.

Page 16: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt16

Wdh. turtle (a_dummy.py)

import turtle as tdef fkt(,,,s)

return

# Objekte erzeugen und initialisierenfenster = t.Screen()fenster.bgcolor('yellow')

hugo = t.Turtle(shape="turtle")hugo.speed(9) # 0: am schnellsten, 10 schnell, 6 normal, 1 langsamhugo.color('blue')hugo.pensize(2)hugo.up()

hugo.goto(0,0)hugo.down()fkt(,,,hugo) t.done() # Warten bis der Benutzer das Fenster schließt

Page 17: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt17

Aufgabe Kochkurve.py

Zeichne mit der Turtle eine Kochkurve der Länge 600 Pixel, wobei die geraden gezeichneten Stücke mindestens die Länge ml haben sollen.

Benutze dazu die Grundstruktur der letzten Folie. Rufe zeichneKoch(laenge, ml, s) rekursiv auf. Wenn laenge <3*ml soll eine Gerade gezeichnet

werden Ansonsten soll die Länge gedrittelt werden und nach

entsprechenden Richtungsänderungen sollen die Teilstücke rekursiv gezeichnet werden.

Page 18: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt18

Kochkurve.py

def zeichneKoch(laenge, ml, s): # laenge = gesamte Länge der Kurve, # ml: Mindestlänge der geraden Teilstücke

# s = turtle

if laenge<3*ml: s.forward(l) else: tiefe-=1 laenge3=laenge/3 zeichneKoch(laenge3, ml, h) s.left(60) zeichneKoch(laenge3, ml, h) s.right(120) zeichneKoch(laenge3, ml, h) s.left(60) zeichneKoch(laenge3, ml, h) return

Page 19: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt19

Schneeflocke.py

Zeichnet man ein gleichseitiges Dreieck und dann iterativ für jede Seite die Kochsche Kurve, erhält man eine Figur, die wie eine Schneeflocke aussieht.

Die Länge der Randkurve wird mit jeder Iteration immer länger: Sie verlängert sich um den Faktor 4/3.

Die Fläche der Grenzkurve ist 1,6*Fläche des gleichseitigen DreiecksBei jedem Iterationsschritt vergrößertsich die Fläche: Beim ersten Schritt um3*(1/9); beim zweiten um 3*4*1/81, beim n-ten Schritt um Damit ist die Flächensumme

nn 1

n

1 3 43 4

9 4 9

n

n 0

3 4 1 3 1 1 3 9 1 8

4 9 4 4 1 4 9 4 4 5 4 5

Page 20: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt20

Rekursives Programm lesen

Was wird bei dem folgenden Beispiel gezeichnet?

def dreieck(laenge, s):

for i in range(3):

s.forward(laenge), s.left(120)

def DF(L, n): if n==0 or L<10: dreieck(L,s)

else:

dreieck(L)

s.up(), s.forward(5), s.left(60)

s.forward(5), s.right(60), s.down()

DF(L-15,n-1,s)

Siehe dreiecke.py

Page 21: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt21

Baum01.py Man nennt eine Figur selbstähnlich, wenn man innerhalb der

Figur eine oder mehrere verkleinerte Kopien derganzen Figur finden kann.

Page 22: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt22

Selbstähnlichkeit Eine rekursive Zeichenprozedur ruft sich selbst im

Anweisungsteil auf. Eine geometrische Figur heißt selbstähnlich, wenn sie sich in

kongruente Teile zerlegen lässt, die ihr alle ähnlich sind. Vergrößern wir eine der Teilfiguren, so ergibt sich das Ganze.

Selbstähnliche Figuren lassen sich mit der Initiator-Generator-Methode erzeugen.

Sie funktioniert wie folgt: In einem Streckenzug I, dem Initiator, wird jede Strecke durch eine Figur G, den Generator, ersetzt. Daraufhin wird jede (oder nur manche) Strecke der erzeugten Figur durch die Generatorfigur G ersetzt. Diese Streckenersetzung wird beliebig lange wiederholt.

Aufgabe: Was ist der Generator bei der Kochkurve, was beim Baum.

Page 23: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt23

Rekursives Programm lesen

if laenge<10: # Initiator

s.forward(laenge), s.backward(laenge)

return

else: # Generator, eine Figur wird ersetzt.

s.forward(laenge)

s.right(45), zeichne_Baum(laenge/2,s)

s.left(90), zeichne_Baum(laenge/2,s)

s.right(45), s.backward(laenge)

returnSiehe Baum01.py

Aufgabe: Erstelle Py-Prg Baum01.py, das den Baum der vorigen Folie zeichnet. Die Verzeigung beträgt links und recht 45°, die Astlänge wird je Schritt halbiert.

Lösung: Siehe Baum.py

Page 24: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt24

Baum0x.py Ändere das Baumprogramm, mache den Baum realistischer. Baum02.py: Die Baumdicke ändern Baum04.py: Links und rechts soll der Baum variert werden. Baum06.py: Bringe den Zufall ins Spiel, d.h. die Baumdicke

und Länge soll sich zufällig ändern. Verwende:import random as rara.gauss(Mittelwert, Stdabweichung) ra.gauss(stuecklaenge, stuecklaenge*zuf) mit zuf etwa 0,3. stuecklaengeA=int(ra.gauss(stuecklaenge, stuecklaenge*zuf))

Wenn Dir das Zeichnen zu lange geht, verwende hugo.tracer(0) # sofort zeichnen

Page 25: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt25

Farn01 Ein Farn ist recht simpel aufgebaut. Der Kern des Programms:

def zeichneFarn(s, hoehe=200.): # Funktionsdefinition if hoehe >3: # falls Zeichenweg groß genug s.forward(hoehe) s.left(25) zeichneFarn(s, hoehe*0.5) # linker Teilbaum s.right(35) zeichneFarn(s, hoehe*0.7) # mittlerer Teilbaum s.right(25) zeichneFarn(s, hoehe*0.4) # rechter Teilbaum s.left(35) s.backward(hoehe) else: s.forward(hoehe) s.backward(hoehe)

Page 26: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt26

Pythagorasbaum.py

Der Initiator ist eine waagrechte Strecke. Der Generator: Ersetze die Strecke durch ein

Quadrat mit einem rechtwinkligen Dreieck c:a:b=5:4:3 auf der Oberseite. Dabei werden die beiden Katheten des Dreiecks wieder durch den Generator ersetzt.

Der Winkel bei der kürzeren Seite ist 1 4sin 53,135

Page 27: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt27

Drachenkurve

Attraktive rekursive Grafiken basieren oft auf dem Orientierungswechsel. Die Drachenkurve entsteht, wenn beim Generator abwechselnd der Haken nach links bzw. nach rechts ausgeführt wird.

Page 28: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt28

Sierpinski-Dreieck

Das Sierpinksi-Dreieck entsteht dadurch, dass man von einem Dreieck iterativ das mittlere Viertel wegnimmt und dies bei den verbliebenden Dreiecken wiederholt.

Siehe auch http://de.wikipedia.org/wiki/Sierpinski-Dreieck

Lese Doku\einfuehrungRekursion.pdf S. 8

Page 29: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt29

Lösung Sierpinski

def sierpinski(s, laenge, stufe, farbe): s.fillcolor(farbe) pos=s.position() s.left(60), s.forward(laenge), s.right(60) s.begin_fill() for i in range(0,3): s.forward(laenge) s.right(120) s.end_fill() if stufe>1: for i in range(0,3): sierpinski(s,laenge/2, stufe-1, farbe) s.forward(laenge), s.right(120) s.up(), s.goto(pos), s.down() return

Page 30: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt30

Türme von Hanoi Das Spiel benutzt drei Stäbe und eine

Anzahl von Scheiben z.B. 9, die auf die Stäbe gesteckt werden können. Anfänglich befinden sich alle Scheiben in absteigender Größe auf einem Stab angeordnet, d.h. die größte ist ganz unten und die kleinste ganz oben.

Die Aufgabe besteht darin, diesen Turm von einem Stab auf einen anderen zu bewegen unter Beachtung der folgenden Regeln:

In einem Zug darf immer nur eine Scheibe bewegt werden. Es kann immer nur die oberste Scheibe eines Stapels bewegt werden. Eine Scheibe kann auf einem anderen Stab nur abgelegt werden, wenn

der Stab leer ist, oder wenn die Scheibe kleiner als die oberste Scheibe des Zielstapels ist.

Lese http://www.python-kurs.eu/tuerme_von_hanoi.php und erstelle das zughörige Programm

Page 31: SFZ FN  Sj. 13/14

Inf K1/2 Sj 13/14 GZG FN W.Seyboldt31

Quellen Fraktale und Chaos (Mandelbrotmenge): Siehe

http://mathematik.ph-weingarten.de/~hafenbrak/docs/chaos06/chaos01.pdf

http://mathematik.ph-weingarten.de/~hafenbrak/docs/chaos06/chaos02.pdf

http://mathematik.ph-weingarten.de/~hafenbrak/docs/chaos06/chaos03.pdf

http://mathematik.ph-weingarten.de/~hafenbrak/docs/chaos06/chaos04.pdf

http://mathestuff.de/rekursion_mit_python https://ddi.ifi.lmu.de/tdi/2013/upload/materialien-

visualisierung-rekursiver-datenstrukturen/ Butterfly (Tkinter) http://www.pythonmania.de/article/pybutt.html Informatikunterlagen http://www.inf-schule.de/ Selbstähnlihckeit, Zusf. Lese

http://stubber.math-inf.uni-greifswald.de/~bandt/talks/gwd07.pdf