Algorithmen & Programmierung Rekursive Funktionen (2)
Arten von Rekursion Direkte Rekursion Den Aufruf einer Funktion direkt aus ihrem Funktionskörper heraus bezeichnet man als direkte Rekursion (so haben wir es bisher gemacht). Indirekte Rekursion Rekursion kann sich auch über mehrere Funktionen erstrecken. Problem Eine Funktion muss dem Compiler bekannt sein, bevor wir sie nutzen (aufrufen) können. Deshalb haben wir bisher genutzte Funktionen immer vor nutzenden Funktionen definiert. Bei indirekter Rekursion lassen sich nutzende und genutzte Funktionen nicht eindeutig anordnen. 477
Deklaration von Funktionen Deklaration einer Funktion Eine Deklaration einer Funktion bedeutet ihre Bekanntmachung, d.h. dass dem Compiler die Existenz einer Funktion mitgeteilt wird, ohne dabei den Funktionskörper zu definieren. Bestandteile einer Deklaration Vorteil Bei einer Deklaration wird lediglich die Schnittstelle, d.h. Name, Rückgabetyp und Parameter der Funktion definiert. Sobald eine Funktion deklariert wurde kann sie genutzt (aufgerufen) werden. Definition der Funktion Wenn eine Funktion deklariert wurde, kann die zugehörige vollständige Funktion an einer beliebigen Stelle im Quelltext definiert werden. 478
Deklaration einer Funktion Deklaration vs. Funktionskopf Die Deklaration einer Funktion entspricht prinzipiell dem Funktionskopf einer vollständigen Funktionsdefinition und wird von einem Semikolon (statt des Funktionsrumpfes) abgeschlossen. Gemeinsamkeiten von Deklaration und Definition Funktionsname, Anzahl, Typ und Anordnung der Parameter müssen übereinstimmen Mögliche Unterschiede Die Benennung der einzelnen Parameter kann sich voneinander unterscheiden. Bei der Deklaration kann die Benennung der Parameter sogar ganz entfallen, es reicht hierbei eine einfache Auflistung der Datentypen der verwendeten Argumente. Prototyp Deklarierte Funktionen werden auch als (Funktions)-Prototyp bezeichnet. 479
Fehlende Deklaration Achtung Trifft ein C-Compiler auf einen Funktionsaufruf, ohne dass die entsprechende Funktion vorher definiert oder deklariert wurde, wird die Funktion implizit deklariert. Da der Compiler zu diesem Zeitpunkt jedoch weder den Rückgabetyp noch die Typen der Parameter dieser Funktion kennt, muss er dafür Annahmen treffen: Merke Der Rückgabetyp implizit deklarierter Funktionen ist immer int Die Typen der Parameter implizit deklarierter Funktionen werden vom Compiler geraten!!! Vor der Verwendung einer Funktion sollte zumindest ein Funktionsprototyp deklariert worden sein. Dadurch werden falsche Annahmen des Compilers über die Datentypen verhindert. Anmerkung Aktuelle Compiler ( C99) erkennen und verhindern teilweise solche Situationen. 480
Indirekte Rekursion Anwendungsbeispiel Aufgabe ist die Entwicklung eines simplen Taschenrechner -Programms, das den Wert eines vollständigen dyadischen Ausdrucks berechnet. Annahmen als Zahlenwerte sind nur nichtnegative einstellige ganze Zahlen zulässig Ausdrücke können mittels Klammerung geschachtelt werden als Operationen sind nur die Grundrechenarten erlaubt das Erkennen verschiedener Fehlerzustände soll möglich sein die Zeichen des dyadischen Ausdrucks werden direkt von der Tastatur gelesen 481
Beispiel Taschenrechner Grammatik in EBNF Ausdruck = Operand Operator Operand Operand = "(" Ausdruck ")" Zahl Zahl = "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" Operator = "+" " " "*" "/" Vereinfachung Da Zahl und Operator nur aus Terminalsymbolen bestehen, können wir sie direkt in Ausdruck und Operand integrieren: Ausdruck = Operand ("+" " " "*" "/") Operand Operand = "(" Ausdruck ")" "0" "1" "2" "3" "4" "5" "6" "7" "8" "9" 482
Beispiele rekursiver Funktionen Fibonacci-Zahlen Pascalsches Dreieck Rekursiver pythagoräischer Baum
Fibonacci Person Fibonacci war ein italienischer Mathematiker (1170-1250). Fibonaccis Hauptwerk Liber abaci führte in Europa die arabischen Ziffern und die Null ein und popularisierte das Dezimalsystem. Problem Fibonacci beschrieb in seinem Buch Liber abaci folgendes Problem: Ein Mann setzt ein Kaninchenpaar in ein Gehege. Diese Kaninchen bekommen jeden Monat ein weiteres Paar. Ein eben geborenes Paar wird nach zwei Monaten fruchtbar. Wie viele Kaninchenpaare gibt es nach einem Jahr in diesem Gehege? 484
Fibonacci-Folge Regelmäßigkeit Wir erkennen, dass sich die Anzahl der Kaninchen eines Monats aus der Summe der Anzahl der Kaninchen der beiden vorherigen Monate ergibt. Fibonacci-Folge Jede Folge deren Glieder sich aus der Summe der beiden vorhergehenden Glieder ergeben, d.h. a n+2 = a n + a n+1 wird Fibonacci-Folge genannt. Standardfolge wir legen a 0 mit 0 und a 1 mit 1 fest und erhalten dann folgende Standardfolge: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377,... 485
Aufrufgraph für fib_rek(6) 486
Eigenschaften der Fibonacci-Folge Goldener Schnitt Der Quotient zweier benachbarter Glieder strebt gegen den goldenen Schnitt: $ Φ = lim a n & % a n 1 ' ) ( Der goldene Schnitt Φ ist ein berühmtes Verhältnis von Strecken in der Architektur. Eine Strecke der Länge l ist durch einen Punkt p im goldenen Schnitt unterteilt, wenn die beiden Teilstrecken im gleichen Verhältnis stehen wie die gesamte Strecke zu der größten der beiden Teilstrecken: p l l p l p = p l p 487
Die Fibonacci-Spirale Die Glieder der Fibonacci-Folge bezeichnen die Seitenlänge von Quadraten. Dabei setzt man für 13 jedes folgende Glied das korrespondierende Quadrat in folgender Reihenfolge aneinander: links 8 2 3 1 1 5 oben rechts unten Werden die einander gegenüberliegenden Ecken 0 1 1 2 3 5 8 13... jedes Quadrats in der gleichen Reihenfolge mit einer Kurve verbunden, erhält man eine Spirale. 488
Eigenschaften der Fibonacci-Zahlen Gegeben seien vier direkt aufeinander folgende Fibonacci-Zahlen f1 bis f4, d.h. für eine beliebige ganz Zahl n 0 gilt: f1 = Fib(n) f2 = Fib(n+1) f3 = Fib(n+2) f4 = Fib(n+3) Ein rechtwinkliges Dreieck lässt sich aus f1 bis f4 wie folgt darstellen: erste Kathete: a = 2 f2 f3 zweite Kathete: b = f1 f4 Hypotenuse: c = f2 2 + f3 2 489
Explizite Berechnung Die Berechnung der Fibonacci-Zahlen ist sogar direkt möglich, d.h. ohne rekursive oder iterative Berechnung vorheriger Fibonacci-Zahlen: Formel von Binet mit Hilfe des goldenen Schnitts Φ und dessen Reziprokem ϕ=φ -1 n-te Fibonacci-Zahl Fib(n) ist dann Φ = 5 +1 2 Fib(n) = Φn ( φ) n 5 = 1 5 %% ' ' && 1+ 5 2 ( * ) n % 1 5 ( ' * & 2 ) n ( * ) Achtung: Bei Berechnungen nach dieser Methode sollte für große n (n>50) die Rechenungenauigkeit reeller Zahlen unbedingt beachtet werden! 490
Beispiele rekursiver Funktionen Fibonacci-Zahlen Pascalsches Dreieck Rekursiver pythagoräischer Baum
Pascalsches Dreieck Anwendung Bisher haben wir das Pascalsche Dreieck verwendet, um schnell beliebige Potenzen von Binomen auszumultiplizieren. 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1 1 5 10 10 5 1 1 6 15 20 15 6 1 1 7 21 35 35 21 7 1 1 8 28 56 70 56 28 8 1 1 9 36 84 126 126 84 36 9 1 492
Pascalsches Dreieck Bildungsprinzip Jeder Wert ist gleich der Summe der beiden rechts und links über ihm stehenden Werte Analogie Die Bildung des Pascalschen Dreiecks basiert auf dem gleichen Prinzip wie die Fibonacci-Folge: Wenn wir das Dreieck linksbündig ausgeben, dann erhalten wir die Matrix P, deren Komponenten wie folgt berechnet werden können: P[i,j] = P[i 1, j 1] + P[i 1, j] für i>0 und j>=0 P[0,0] = 1 alle sonstigen Komponenten der Matrix sind 0 und gehören zu keinem Element des Pascalschen Dreiecks 1 0 0 0 0 1 1 0 0 0 1 2 1 0 0 1 3 3 1 0 1 4 6 4 1 493
Pascalsches Dreieck 0 1 2 3 4 5 6 j 0 1 0 0 0 0 0 0 0 1 1 1 0 0 0 0 0 0 2 1 2 1 0 0 0 0 0 3 1 3 3 1 0 0 0 0 4 1 4 6 4 1 0 0 0 5 1 5 10 10 5 1 0 0 6 1 6 15 20 15 6 1 0 i 1.............. Weitere Eigenschaften Die Summe der nach rechts geneigten Diagonalen ergeben die Fibonacci-Folge In der zweiten Spalte befinden sich die natürlichen Zahlen In der dritten Spalte befinden sich die Dreieckszahlen Die Summe der n-ten Zeile entspricht 2 n 494
Beispiele rekursiver Funktionen Fibonacci-Zahlen Pascalsches Dreieck Rekursiver pythagoräischer Baum
Rekursiver pythagoräischer Baum Bedeutung Ein pythagoräischer Baum ist ein rekursiver grafischer Algorithmus, der ein Gebilde erzeugt, das nach mehreren Rekursionsschritten einem Baum ähnelt. Algorithmus man errichtet über zwei gegebenen Punkten ein Quadrat die Oberseite des Quadrats bildet die Grundseite eines Dreiecks, das mit einer beliebigen Höhe gezeichnet wird. die Eckpunkte der beiden Schenkel des Dreiecks bilden die Eingabe für den erneuten Ablauf des Algorithmus Terminierung Damit der Algorithmus terminieren kann, muss eine maximale Rekursionstiefe vorgegeben werden. 496
Rekursiver pythagoräischer Baum 497
Ende der Vorlesung