11. Rekursion Motivation: Rekursive Definitionen Rekursive Definitionen sind in der Mathematik verbreitet: n! = xn = n ∑a i =1 i = { 1 falls n=0 n*(n-1)! falls n>0 n = k { 1 x*xn-1 falls n=0 falls n>0 n = k { 0 falls n<=0 n −1 ∑ ai + an i =1 falls n>0 Dr. Norbert Spangler / Programmieren I { 1 n +1− k n falls k>0 k k − 1 { 1 { n Fn-1+Fn-2 Fn = falls k=0 falls k=0, n=k n − 1 n − 1 + falls n > k>0 k k − 1 falls n=0,1 falls n>1 01.12.2013 2 Motivation: Rekursive Strukturen Sequenzielle Struktur Baumstruktur Struktur = Struktur Struktur = Struktur -> Dr. Norbert Spangler / Programmieren I = Struktur -> Struktur 01.12.2013 3 Motivation: Rekursive Grafiken Bilder die sich selbst enthalten Dr. Norbert Spangler / Programmieren I 01.12.2013 4 Programmierung Die Programmierung einer rekursiven Funktion in C++ stellt kein Problem dar. In einer Funktion kann dieselbe Funktion wieder aufgerufen werden int fakultaet(int n) { if ( n==0 ) return 1; else return n*fakultaet(n-1); } double summe(int n,double a[]) { if ( n<=0 ) return 0; else return summe(n-1,a)+a[n-1]; } int fibonacci (int n)//rekursiv { if ( n==0 ) return 0; else if ( n==1 ) return 1; else return fibonacci(n-1)+fibonacci(n-2); } Rekursive Lösungen lassen sich in der Regel "kürzer/eleganter" programmieren als iterative Lösungen mit Schleifen. int fibonacci(int n)//Schleife {//iterativ if ( n==0) return 0; else if ( n==1) return 1; else { int a=0,b=1,c; for ( int i=2;i<=n;i++) { c=b+a; //Addition a=b; b=c; } } } Bzgl. Aufwand bei Fibonacci: Was ist besser? Kriterium : Anzahl der Additionen Dr. Norbert Spangler /Informatik 01.12.2013 5 Beispiel: Fibonaccizahlen / Aufrufbaum für fibonacci(6) 6 Aufwand Rekursion Additionen 12 Funktionsaufrufe 25 Fibonacci(0): 5 x Fibonacci(1): 8 x Fibonacci(2): 5 x Fibonacci(3): 3 x Fibonacci(4): 2 x Fibonacci(5): 1 x Fibonacci(6): 1 x 5 4 + + 2 4 2 Aufwand Schleife Additionen 5 Funktionsaufrufe 1 + 3 3 + + 11 + 0 1 + 0 2 1 + 0 Dr. Norbert Spangler / Programmieren I 2 + 3 + + 11 + 0 2 1 + 0 1 Schleifen verursachen "weniger" Aufwand (Speicher/Zeit) 01.12.2013 6 Zusammenfassung Rekursive Programme sind in der Regel kürzer/einfacher zu formulieren Iterationen (Schleifen) sind in der Regel effizienter (Overhead durch Funktionsaufrufe fehlt) Häufig lassen sich Rekursionen durch Iterationen ersetzen. „Faustformel“ für „übliche“ Aufgaben: Jede Aufgabe lässt sich rekursiv lösen! Jede Rekursion lässt sich mittels Schleife realisieren! Gegenbeispiel Ackermannfunktion: keine Lösung mit Schleife A(0,n) = n+1 A(m+1,0) = A(m,1) A(m+1,n+1) = A(m,A(m+1,n) Dr. Norbert Spangler /Informatik 01.12.2013 7 Definition Rekursion (lat. recurrere) bedeutet Selbstbezüglichkeit d.h. etwas verweist auf sich selbst. Interpretation a: Umkehrung der Induktion Eine Aufgabe lässt sich zerlegen in eine „einfach“ zu lösendes separate Teilaufgabe und eine weitere Aufgabe, welches vom selben Typ ist wie die Ausgangsaufgabe mit geringere Komplexität bzw. geringeren Umfang. Beispiele: einfach fakultät(n) = n * Sortiere(n Zahlen) = größtes suchen + Suche(n Zahlen) = letztes Element testen + Summe(n Zahlen) = letzte Zahl + Dr. Norbert Spangler /Informatik derselbe Typ/geringerer Umfang fakultaet(n-1) Sortiere(n-1 Zahlen) Suche(n-1 Zahlen) Summe(n-1 Zahlen) 01.12.2013 8 Definition Rekursion (lat. recurrere) bedeutet Selbstbezüglichkeit d.h. etwas verweist auf sich selbst. Interpretation b: Divide and Conquer (Teile und Herrsche) Eine Aufgabe lässt sich zerlegen in zwei Teilaufgaben welche vom selben Typ sind wie die Ausgangsaufgabe aber mit geringere Komplexität bzw. geringeren Umfang. Die Gesamtlösung wird aus den 2 Teillösungen zusammengesetzt. Für „ganz“ einfache Fälle ist die Aufgabe direkt lösbar. Algorithmen Sortieren Sortiere (Teil 1) Sortiere (Teil 2) Setze die 2 sortierten Teilen zusammen Einfacher Fall: höchstens 1 Element Suchen (Binäre Suche) Test für linke Hälfte->Suche(linke Hälfte) Test für rechte Hälfte-> Suche(rechte Hälfte) gefunden Einfacher Fall: kein Element Dr. Norbert Spangler /Informatik 01.12.2013 9 Beispiel: Suche Prinzip: Umkehrung Induktion Man testet ob es das letzte Element ist und sucht, falls nicht gefunden, im Rest mit derselben Technik. Ist kein Element mehr da zum Vergleich, dann wird nichts gefunden. int suche (int n, int a[],int element) { if ( n==0 ) return -1;//nicht gefunden else if ( a[n-1] ==element ) return n-1;//gefunden else return suche(n-1,a,element);// im Rest weitersuchen } Dr. Norbert Spangler /Informatik 01.12.2013 10 Sortieren (Quicksort klassisch) Prinzip: Teile und Herrsche Dr. Norbert Spangler / Programmieren I 01.12.2013 11 Türme von Hanoi Start Hilfe Ziel n Scheiben Alle Scheiben müssen von Start nach Ziel unter Verwendung von Hilfe umgelegt werden, wobei immer nur eine Scheibe bewegt werden und eine größere Scheibe nie auf eine kleinere gelegt werden darf. Tuerme(int anzahl,char start,char hilfe,char ziel) { if ( anzahl>0 ) { Tuerme(anzahl-1,start,ziel,hilfe); cout<<"von "<<start<<" nach "<<ziel<<endl; Tuerme(anzahl-1,hilfe,start,ziel); } } Dr. Norbert Spangler /Informatik 01.12.2013 12 Pythagoräischer Baum Baum(Seite) falls Seite zu kurz -> Ende errichte über der Seite ein Quadrat zeichne über der gegenüberliegenden Seite ein Dreieck mit vorgegebenen Winkeln Baum(neue Dreieckseite1) Baum(neue Dreieckseite2) Dr. Norbert Spangler /Informatik 01.12.2013 13 Weitere rekursive Ansätze Listenverarbeitung(Arrays) Bearbeite(Liste) = Bearbeite(letztes Element) + Bearbeite(Rest) oder Bearbeite(erstes Element) + Bearbeite(Rest) Summation Summe(Array) = letztes Element+Summe(Rest) Potenzreihen neuer Summand=letzter Summand * Faktor n j ∑ a j x : Berechne j =0 (− x 2 ) j ( − x 2 ) j +1 : ∑ 2( j + 1)! j =0 2 j! n Faktor = = Dr. Norbert Spangler / Programmieren I a j +1 x j +1 ajx (− x 2 ) j 2 j! j = a j +1 aj x (− x 2) * ( 2 j + 1)( 2 j + 2) 01.12.2013 14 Generelle Vorgehensweise Allgemeine Fragestellungen bei einer gegebenen Aufgabe: Ist die Aufgabe rekursiv und/oder iterativ lösbar? Was ist das Schema der Rekursion? Lässt sich die Aufgabe in "2 Hälften zerlegen" und dann zusammensetzen? Lässt sich die Aufgabe um ein Objekt reduzieren und dann zusammensetzen? Was ist wichtig: Einfachkeit des Programms (Lesbarkeit) Termin der Fertigstellung Effizienz Die Programmierung rekursiv definierter Funktionen stellt kein Problem dar, da man sie in der Regel direkt programmieren kann. Allerdings sind iterative Lösungen nicht immer direkt erkennbar bzw. teilweise auch unmöglich (Ackermann, Türme von Hanoi). Dr. Norbert Spangler / Programmieren I 01.12.2013 15