Rekursive Algorithmen Recursion Ferd van Odenhoven Fontys Hogeschool voor Techniek en Logistiek Venlo Software Engineering 6. Oktober 2014 ODE/FHTBM Recursion 6. Oktober 2014 1/18 Rekursive Algorithmen 1 ODE/FHTBM Rekursive Algorithmen Anwendung mathematischer Relationen Richtigkeit Beispiele Schlussbemerkungen Recursion 6. Oktober 2014 2/18 Rekursive Algorithmen Anwendung mathematischer Relationen Richtigkeit Beispiele Schlussbemerkungen Anwendung mathematischer Relationen Gegeben sei eine mathematische Funktion z. B. für Fakultät N: N! = N ∗ (N − 1)! , N≥0 , (0! = 1). (1) Programm: static int factorial ( int N ) { if ( N ==0) return 1; return N * factorial (N -1); } ODE/FHTBM Recursion 6. Oktober 2014 3/18 Rekursive Algorithmen Anwendung mathematischer Relationen Richtigkeit Beispiele Schlussbemerkungen Einige Anmerkungen Dieselbe Berechnung ließe sich auch mit einer einfachen for-Schleife durchführen. Ein rekursives Programm lässt sich immer in ein nicht-rekursives umwandeln und umgekehrt. Manchmal ist es viel einfacher, ein rekursives Programm zu entwerfen als eine korrekte Schleife. ODE/FHTBM Recursion 6. Oktober 2014 4/18 Rekursive Algorithmen Anwendung mathematischer Relationen Richtigkeit Beispiele Schlussbemerkungen Richtigkeit Durch Induktion beweisen, dass die Rekursion funktioniert; z. B. for N!: static int factorial ( int N ) { if ( N ==0) return 1; return N * factorial (N -1); } Sie berechnet 0! (Grundschritt) Da sie (N − 1)! berechnet, berechnet sie N! (Induktion) ODE/FHTBM Recursion 6. Oktober 2014 5/18 Rekursive Algorithmen Anwendung mathematischer Relationen Richtigkeit Beispiele Schlussbemerkungen Richtigkeit Wir werden keine formalen Richtigkeitsbeweise verwenden, es sollen nur zwei grundlegende Eigenschaften erfüllt sein: Ein Basisfall muss explizit erfüllt sein (Grundschritt) Rekursiver Aufruf mit kleineren Argumenten (Induktion) In diesem Programm gibt es keine Induktion im oben definierten Sinn: static int puzzle ( int N ) { if ( N == 1) return 1; if ( N %2 == 0) return puzzle ( N /2); else return puzzle (3* N +1); } ODE/FHTBM Recursion 6. Oktober 2014 6/18 Rekursive Algorithmen Anwendung mathematischer Relationen Richtigkeit Beispiele Schlussbemerkungen Puzzle-Beispiel puzzle (3) puzzle (10) puzzle (5) puzzle (16) puzzle (8) puzzle (4) puzzle (2) puzzle (1) ODE/FHTBM Recursion 6. Oktober 2014 7/18 Rekursive Algorithmen Anwendung mathematischer Relationen Richtigkeit Beispiele Schlussbemerkungen Euklidischer Algorithmus Finden Sie den größten gemeinsamen Teiler von zwei Zahlen M und N, wobei M > N. Wenn M und N einen gemeinsamen Teiler p haben, ist p auch ein Teiler von (M mod N). Der größte mögliche Wert von p ist N; Wenn an irgend einer Stelle p gleich N ist, haben wir: (M mod N) = 0, und der Algorithmus muss sich beenden! ODE/FHTBM Recursion 6. Oktober 2014 8/18 Rekursive Algorithmen Anwendung mathematischer Relationen Richtigkeit Beispiele Schlussbemerkungen Euklidischer Algorithmus: recursive method /* * * pre : M > N * post : M = gcd (M , N ) */ int gcd ( int M , int N ) { if ( N ==0) return M ; return gcd ( N , M % N ); } ODE/FHTBM Recursion 6. Oktober 2014 9/18 Rekursive Algorithmen Anwendung mathematischer Relationen Richtigkeit Beispiele Schlussbemerkungen Euklidischer Algorithmus: non recursive method /* * * * */ int { pre : M > N post : M = gcd (M , N ) gcd ( int M , int N ) int h ; while ( N != 0) { h = N; N = M % N; M = h; } return M ; } ODE/FHTBM Recursion 6. Oktober 2014 10/18 Rekursive Algorithmen Anwendung mathematischer Relationen Richtigkeit Beispiele Schlussbemerkungen Euklidischer Algorithmus gcd (314159 , 271828) gcd (271828 , 42331) gcd (42331 , 17842) gcd (17842 , 6647) gcd (6647 , 4458) gcd (4458 , 2099) gcd (2099 , 350) gcd (350 , 349) gcd (349 , 1) gcd (1 ,0) ODE/FHTBM Recursion 6. Oktober 2014 11/18 Anwendung mathematischer Relationen Richtigkeit Beispiele Schlussbemerkungen Rekursive Algorithmen Baum für arithmetischer Ausdruck × + 5 × 7 × + 9 8 4 6 Abbildung : Beispiel: Baum für arithmetischer Ausdruck ODE/FHTBM Recursion 6. Oktober 2014 12/18 Rekursive Algorithmen Anwendung mathematischer Relationen Richtigkeit Beispiele Schlussbemerkungen Präfix-Ausdrücke evaluieren Das rekursive Programm evaluiert die Präfix-Ausdrücke aus dem vorangehenden Kapitel, z. B.: ∗ + 7 ∗ ∗ 4 6 + 8 9 5 Die Überlegung ist: Die Rekursion im linken Baum und im rechten Baum werden gleichzeitig initiiert! Die nächste Folie zeigt Details. Vorsicht! Gültige arithmetische Operationen sind + und ∗, aber nicht − oder /. ODE/FHTBM Recursion 6. Oktober 2014 13/18 Rekursive Algorithmen Anwendung mathematischer Relationen Richtigkeit Beispiele Schlussbemerkungen Programm eval int eval () { int i = 0; int x = 0; if ( expr [ i ] == ’+ ’) { i ++; return eval () + eval (); } if ( expr [ i ] == ’* ’) { i ++; return eval () * eval (); } while (( expr [ i ] >= ’0 ’) && ( expr [ i ] <= ’9 ’ )) { x = 10* x + ( expr [ i ++] - ’0 ’ ); } return x ; } ODE/FHTBM Recursion 6. Oktober 2014 14/18 Rekursive Algorithmen Anwendung mathematischer Relationen Richtigkeit Beispiele Schlussbemerkungen Präfix-Beispiel eval () * + 7 * * 4 6 + 8 eval () + 7 * * 4 6 + 8 eval () 7 eval () * * 4 6 + 8 9 eval () * 4 6 + 8 eval () 4 eval () 6 return 24 = 4*6 eval () + 8 9 5 eval () 8 eval () 9 return 17 = 8+9 return 408 = 24*17 return 415 = 7+408 eval () 5 return 2075 = 415*5 ODE/FHTBM Recursion 9 5 9 5 5 9 5 6. Oktober 2014 15/18 Rekursive Algorithmen Anwendung mathematischer Relationen Richtigkeit Beispiele Schlussbemerkungen Schlussbemerkungen: Rekursionen und Bäume Die Tiefe der Rekursion: ist die maximale Verschachtelungstiefe und hängt ab von der Eingabe. Rekursive Programme: brauchen eine Stackmaschine, deren Speicherverbrauch proportional ist zur Tiefe der Rekursion. Datenstrukturen: sind inhärent rekursiv, wenn die Objekte darin auf ein oder mehrere Objekte vom selben Typ verweisen. Rekursive Programme: sind deshalb die Implementierungen, die sich für solche Datenstrukturen, z. B. verkettete Listen, anbieten. Beachten Sie: dass die Tiefe der Rekursion der Länge einer verketteten Liste entsprechen kann. ODE/FHTBM Recursion 6. Oktober 2014 16/18 Rekursive Algorithmen Anwendung mathematischer Relationen Richtigkeit Beispiele Schlussbemerkungen Rekursive Funktionen für verkettete Listen 1 int count ( Node h ) { if ( h == null ) return 0; return 1 + count ( h . next ); } void traverse ( Node h ) { if ( h == null ) return ; h . item . visit (); traverse ( h . next ); } ODE/FHTBM Recursion 6. Oktober 2014 17/18 Rekursive Algorithmen Anwendung mathematischer Relationen Richtigkeit Beispiele Schlussbemerkungen Rekursive Funktionen für verkettete Listen 2 void traverseR ( Node h ) { if ( h == null ) return ; traverseR ( h . next ); h . item . visit (); } Node remove ( Node h , Item v ) { if ( h == null ) return null ; if ( eq ( h . item , v )) return remove ( h . next , v ); h . next = remove ( h . next , v ); return h ; } ODE/FHTBM Recursion 6. Oktober 2014 18/18