Einführung in die Informatik I Fortgeschrittene Rekursion Prof. Dr. Nikolaus Wulff
Problematische Rekursion Mittels Rekursion lassen sich Spezifikationen recht elegant und einfach implementieren. Leider sind jedoch die so erhaltenen Lösungen meist nicht sehr effizient, was den Speicherverbrauch und die Laufzeit betrifft. Da sich jede primitive Rekursion durch eine iterative Lösung mittels Schleifen darstellen lässt, gilt es einen strukturierten Weg zu finden, um eine rekursive Lösung in eine Iterative zu überführen. Ein erster Schritt hierzu die Endrekursion, die eine effizientere Implementierung gestattet. Prof. Dr. Nikolaus Wulff Informatik I 2
Endrekursion Eine Rekursion heißt endrekursiv, wenn der Aufruf der Rekursion die letzte Aktion zur Berechnung der rekursiven Funktion f ist. Endrekursive Funktionen zeigen ein besseres Verhalten hinsichtlich des Speicherbedarfs für lokale Variablen und den Stack, als normal rekursive Funktionen, die nach der Rekursion noch weitere Berechnungen vornehmen. Am Beispiel der rekursiven Fakultätsberechnung soll das Prinzip verdeutlicht werden. Prof. Dr. Nikolaus Wulff Informatik I 3
Rekursive Fakultät int factorial(int n) { if(n<=1) return 1; return n*factorial(n-1); Diese Implementierung der Fakultät ist nicht endrekursiv, da nach der Rekursion noch eine Multiplikation mit dem Argument n erfolgt. Sie ergibt sich z.b. für n=4 die Aufruffolge 4!: f(4) = 4 f(3) => f(4)=4 6=24 f(3)= 3 f(2) => f(3)=3 2=6 f(2) = 2 f(1) => f(2)=2 1=2 LIFO f(1)=1 Berechnung Prof. Dr. Nikolaus Wulff Informatik I 4
Endrekursion mittels Hilfsfunktion int g_factorial(int n, int fac) { if (n<=1) return fac; return g_factorial(n-1, fac*n); int factorial(int n) { return g_factorial(n, 1); Durch Einführen der Hilfsfunktion g_factorial wird die nachträgliche Multiplikation vermeiden. Die Rekursion ist nun endrekursiv. f(4) = g(4,1) = g(3,1 4) g(3,4) = g(2,4 3) g(2,12) = g(1,12 2) = 24 Prof. Dr. Nikolaus Wulff Informatik I 5
Nichtlineare Rekursion Bei der Endrekursion entfallen viele der Zwischenergebnisse, die auf dem Stack vorgehalten werden und die Funktion kann am Ende der Rekursion direkt terminieren. An dem einfachen Beispiel der Fakuktät kommt dieser Vorteil noch nicht so deutlich zum Tragen, da hier die Rekursion linear verläuft, anders wird dies bei nichtlinearen Rekursionen. Eine nichtlineare Rekursion liegt vor, wenn im Rumpf der Definition von f mehr als ein rekursiver Aufruf ist. Prof. Dr. Nikolaus Wulff Informatik I 6
Nichtlineare Rekursion Ein besonders schlechtes Lauftzeitverhalten hat die nichtlineare Rekursion, wie sie z.b. bei den Fibonacci Zahlen: 1, 1, 2, 3, 5, 8, 13, 21, 34,... vorkommt. f n := f n 1 f n 2 f 1 := f 2 :=1 Die Aufruffolge zeigt, dass viele Fibonacci Zahlen mehrfach berechnet werden und ~2 n Auswertungen notwendig sind: f n f n-1 f n-2 f n-2 f n-3 f n-3 f n-4 f n-3 f n-4... Prof. Dr. Nikolaus Wulff Informatik I 7
Auflösen der Rekursion Es ist offensichtlich, dass diese rekursive Lösung nicht effizient ist und viele der Zahlen f k unnötig oft berechnet werden. Zum Berechnen von f k werden lediglich die direkten Vorgängerzahlen f k-1 und f k-2 benötigt. Wenn der Algorithmus diese bei der Berechnung nur einmal berechnet und ab dann wiederverwendet, so wird sich die Rekursion wesentlich verkürzen. Hierzu wird eine Hilfsfunktion g eingeführt, welche die Fibonacci-Zahl f k aus den beiden vorhergehenden Funktionswerten mit linearer Endrekursion berechnet. Prof. Dr. Nikolaus Wulff Informatik I 8
Linearisierung der Rekursion int g_fibonacci(int n, int fk1, int fk2) { if (n<=1) return fk1+fk2; return g_fibonacci(n-1, fk2+fk1, fk1); int fibonacci(int n) { return g_fibonacci(n-1,1,0); Aus der nichtlinearen Rekursion ist mittels der Hilfsfunktion g eine Endrekursion geworden. Diese Lösung hat eine lineare Laufzeit ~n im Vergleich zur ursprünglichen rekursiven Lösung ~2 n Prof. Dr. Nikolaus Wulff Informatik I 9
Endrekursion > Iterative Lösung Ist erst einmal eine endrekursive Lösung gefunden worden, so ist es meistens leicht, daraus eine iterative Lösung zu entwickeln, die hinsichtlich der Laufzeit immer die bessere Variante darstellt. Die generische Struktur einer linearen Rekursion ist: g n fallst n f n ={ h n, f n 1 sonst Hierbei ist T(n) die terminierende Bedingung und im Spezialfall h(n,y)=y=f(n) ergibt sich die Endrekursion, die sich durch ein LOOP Programm, d.h eine einfache n-fache for-schleife berechnen lässt. Prof. Dr. Nikolaus Wulff Informatik I 10
Iterative Lösung unsigned fibonacci(unsigned n) { unsigned f, fk1=1, fk2=0; while (--n) { f = fk1 + fk2; fk2 = fk1, fk1 = f; return fk1; Diese iterative Lösung hat die Hilfsfunktion g durch eine Schleife ersetzt und besitzt sowohl eine optimale Laufzeit als auch den minimalen Speicherverbrauch. Prof. Dr. Nikolaus Wulff Informatik I 11
Wechselseitige Rekursion unsigned even(unsigned n) { if (n==0) return 1; return odd(n-1); unsigned odd(unsigned n) { if(n==0) return 0; return even(n-1); Indirekte Rekursion liegt vor, wenn eine Funktion f eine Funktion h aufruft, die direkt oder indirekt wiederum eine Rekursion der Funktion f aufruft, wie hier am Beispiel der Funktionen even und odd, die berechnen ob eine Zahl n gerade oder ungerade ist. Prof. Dr. Nikolaus Wulff Informatik I 12
Weitere Rekursionen Das letzte Beispiel ist ziemlich künstlich, ob eine Zahl n gerade oder ungerade ist, lässt sich mit der Modulo Operation n%2 == 1 viel einfacher und schneller herausbekommen. Das Bisektionsverfahren zur Nullstellensuche oder dem Zahlenraten lässt sich rekursiv durchführen. Rekursion wird nicht nur für rein mathematische Aufgaben verwendet, sondern auch bei Strategiespielen, die sich im weitesten Sinne auf Rekursion zurückführen lassen: Wolf, Schaf und Kohl mit Floss Das acht Damen Problem Prof. Dr. Nikolaus Wulff Informatik I 13
Graphische Rekursionen Mittels Rekursion lassen sich graphische Algorithmen entwickeln wie z.b. die fraktale Koch Kurve (1904). Eine Strecke wird in drei gleichlange Teile zerlegt. Das mittlere Teilstück wird verdoppelt und die beiden werden mit einem 60 Winkel eingefügt. Anschließend wird diese Vorschrift rekursiv auf alle Teilstücke angewandt. 1. Rekursion 0. Rekursion Prof. Dr. Nikolaus Wulff Informatik I 14
Entwicklung der Koch Kurve Rekursionstiefe: 1 2 3 4 5 Prof. Dr. Nikolaus Wulff Informatik I 15
Hilbert Kurve Die Kurve entsteht durch vier Zeichenfunktionen, die sich wechselseitig rekursiv aufrufen... Prof. Dr. Nikolaus Wulff Informatik I 16
Entwicklung der Hilbert Kurve 1 2 3 4 5 6 Mit zunehmender Rekursionstiefe wird die Kurve immer filigraner und berührt im Limes jeden Punkt der Fläche und füllt diese vollständig aus... Prof. Dr. Nikolaus Wulff Informatik I 17