CS1005 Objektorientierte Programmierung Bachelor of Science (Informatik) Iteration und Rekursion Seite 1 Th Letschert While-Schleife Schleife, While-Schleife Schleife: Eine Anweisung / Anweisungsfolge wiederholt ausführen While-Schleife Zahl der Durchläufe wird vom Wert einer Bedingung gesteuert Allgemeine Form auch: „Schleifenkörper“ while ( Bedingung ) Anweisung(-sfolge) Beispiel int zaehler = 0; System.out.println("Die ersten 15 Quadratzahlen:"); while (zaehler < 15) { zaehler = zaehler + 1; System.out.println(zaehler + " -> " + zaehler * zaehler); } Seite 2 While-Schleife Schleife, While-Schleife Beispiel: Die Summe der ersten n natürlichen Zahlen int n = 5; int sum = 0; int i = 0; Angenommen n=5: Welche Variablen haben welche Werte im Laufe einer Berechnung? while (i < n) { i = i + 1; sum = sum + i; } System.out.println("Summe 1 ... " + n + " = " + sum); n i sum 5 5 5 0 1 2 ··· 0 1 3 Angenommen die Anweisungen in der Schleife werden vertauscht. Was passiert? Ist Berechnung noch korrekt ? Wenn nein: warum nicht ? Seite 3 Schleife Schleife und Wertverlaufstabelle Tabelle mit Variablen und ihren wechselnden Werten n i sum 5 0 0 5 1 1 5 2 3 5 3 6 5 4 10 5 5 15 Schleifenende Einstieg erste Zeile n = 5 sum = 0 i = 0 Schleifenkörper von Zeile zu Zeile while ( i < n ) { i = i+1 sum = sum + i } Schleifenende Bedingung nicht mehr erfüllt, keine weitere Zeile Seite 4 Schleife Konstruktion der Schleife aus einer Tabelle Welche Wertefolgen gibt es In welcher Variablen wird welche Folge gespeichert Wann endet die Berechnung n i sum 5 0 0 5 1 1 5 2 3 5 3 6 5 4 10 5 5 15 Schleifenende Initialisierung: Wie kommt man zu Zeile 1 Schleifenkörper: Wie kommt man von Zeile zu Zeile Schleifenende: Bedingung mit gestoppt wird Seite 5 n = 5 sum = 0 i = 0 while ( i < n ) { i = i+1 sum = sum + i } While-Schleife Übung Die Summe der ersten n natürlichen Zahlen kann nach einer Formel berechnet werden: n ∑ i = ½ n * (n+1) i=1 ➢ ➢ ➢ Schreiben Sie eine Funktion, die sumF(x), die die Summe der ersten x natürlichen Zahlen nach dieser Formel berechnet. Schreiben Sie eine Funktion sumW(x), die die Summe der ersten x natürlichen Zahlen durch Aufsummieren berechnet. Schreiben Sie eine Schleife in der die Summe der der ersten n Zahlen für n = 1 · · · 100 jeweils mit beiden Funktionen berechnet und ausgegeben wird. Seite 6 Do-While-Schleife Schleife, Do-While-Schleife Kontrolle am Ende eines Durchlaufs int n = 5; int sum = 0; int i = 0; do { i = i + 1; sum = sum + i; } while (i < n); Der erste Durchlauf findet in jedem Fall statt ! Seite 7 For-Schleife For-Schleife Die Schleife durchläuft eine Folge von Werten Allgemeine Form for ( Start-Anw; Bedingung; Inkrement-Anw ) Anweisung(-sfolge) Beispiel for ( int i = 0; i <= n; i=i+1 ) { sum = sum + i; } Seite 8 For-Schleife For-Schleife Ausführung for ( Start-Anw Bedingung; Inkrement-Anw ) Anweisung(-sfolge) entspricht: Start-Anw while ( Bedingung ) { Anweisung(-sfolge) Inkrement-Anw } Beispiel for ( int i = 0; i <= n; i=i+1 ) { sum = sum + i; } Seite 9 ≈ int i = while ( sum i = } 0 i <= n ) { = sum + i; i + 1; For-Schleife Übung Modifizieren Sie Ihr Programm zur Berechnung der Summe der ersten n natürlichen Zahlen derart, dass nur For-Schleifen verwendet werden. Seite 10 Schleife Übung Schleifenkonstruktion Berechne in einer Funktion den Wert a = a0 + a1 + · · · an mit a0 = 0, und ai = ai-1 + c für ein vorgegebenes n und c (Funktionsparameter). Gehen Sie systematisch vor: 1. Berechnen Sie ein Beispiel „händisch“. 2. Welche Werte / Wertefolgen müssen berechnet werden? 3. In welchen Variablen werden diese abgespeichert? 4. Erstellen Sie eine Wertverlaufstabelle. 5. Konstruieren Sie die Schleife nach der Wertverlaufstabelle. Seite 11 Schleife Schleifenkontrolle break - Anweisung beendet die Schleife continue -Anweisung bricht den aktuellen Schleifendurchlauf ab: weiter (continue !) mit dem nächsten Durchlauf Seite 12 Schleife Beispiel Scanner scan = new Scanner(System.in); int n = 0; do { System.out.println("Bitte positive Zahl, Abbruch mit 0"); n = scan.nextInt(); if (n < 0) continue; if (n == 0) break; int sum = 0; int i = 0; while (i < n) { i = i + 1; sum = sum + i; } System.out.println("Summe 1 ... " + n + " = " + sum); } while (true); Seite 13 Rekursion Seite 14 Rekursion Rekursion Ursprung lat. recurrere ~ zurücklaufen rekursive Definition Definition mit Bezug auf sich selbst Ein Konzept wird rekursiv definiert, wenn in der Definition des Konzepts dieses selbst vorkommt. Beispiel rekursiv definierter Algorithmus: Gehe-zum-Erdgeschoss: Bist Du im Erdgeschoss, dann bist du fertig, Sonst: gehe ein Stockwerk tiefer und führe dann den Algorithmus Gehe-zum-Erdgeschoss aus. Das zu erklärende Konzept ist Gehe-zum-Erdgeschoss. In seiner Erklärung (=Definition) kommt Gehe-zum-Erdgeschoss selbst vor. Was passiert, wenn der Algorithmus im Keller ausgeführt wird? Seite 15 Rekursion Beispiel rekursive Definition: Fakultätsfunktion n! Das zu erklärende Konzept ist ! . In seiner Erklärung (=Definition) kommt ! selbst vor. ● ● 0! = 1 n! = (n-1)! * n als Funktionsdefinition: Das zu erklärende Konzept ist fak . In seiner Erklärung (=Definition) kommt fak selbst vor. ● ● fak(0) = 1 fak(n) = fak(n-1)*n oder ● 1 n=0 fak(n-1)*n sonst fak(n) = Seite 16 Rekursion Rekursive Funktionsdefinition = (rekursiver) Algorithmus Jede rekursiv definierte Funktion kann als rekursiv definierter Algorithmus verstanden werden. – Definition der Funktion fak(n): fak(0) = 1 fak(n) = fak(n-1) * n – Algorithmus zur Berechnung von fak(n): Falls n = 0 ~> Resultat = 1 Sonst 1. Erzeuge Gehilfe fakn-1 2.Lasse Gehilfen berechnen fakn-1(n-1) ~> (n-1)! 3.nimm das Ergebnis (n-1)! des Gehilfen und multipliziere es mit n ~> (n-1)!*n Seite 17 Rekursion Rekursion indirekte rekursive Definition (indirekte Rekursion) Definition mit indirektem Bezug auf sich selbst Beispiel gerade / ungerade ● ● ● ● gerade(0) = true gerade(n) = ungerade(n-1) 0 ist gerade n ist gerade, wenn n-1 ungerade ist ungerade(0) = false ungerade(n) = gerade(n-1) 0 ist nicht ungerade n ist ungerade, wenn n-1 gerade ist Seite 18 Rekursion Rekursion in Programmiersprachen Rekursion in Programmiersprachen Rekursion ab ~ 1960 von allen (relevanten) Programmiersprachen unterstützt E.W. Dijkstra: Pinonier der Informatik. Entwickelte u.a. Verfahren zum Übersetzen u. Ausführen rekursiver Funktionen Ab ~1970 als wichtige Kontrollstruktur allgemein anerkannt und eingesetzt. Rekursion in Java Beispiel Fakultätsfunktion package rekursion; public class Fakultaet { private static int fak(int x) { if (x == 0) { return 1; } else { return fak(x-1)*x; } } n! = 1 falls n=0 (n-1)!*n falls n>0 undefiniert sonst public static void main(String[] args) { System.out.println(fak(5)); } } Seite 19 Übung: fak mit bedingtem Ausdruck? Rekursion Beispiel Die Summation 1 bis n n sumBis(n) =∑ i i=0 kann rekursiv definiert werden. Wie? Seite 20 Rekursion und Iteration int sumBisIterativ(int n) { int sum = 0; for ( i = 0; i <= n; i = i+1 ) { sum = sum + i; } return sum; } Algorithmen, die mit Schleifen statt Rekursion arbeiten, nennt man iterativ. int sumBisRekursiv(int n) { return n == 0 ? 0 : sumBisRekursiv(n-1)+n; } Seite 21 Rekursion und Iteration Beispiel : Berechnung der Exponentionfunktion / rekursiv Exponentialfunktion x 2 3 e =1 x x /2! x / 3!··· Welche Beziehung besteht zwischen den Summengliedern: ➢ Kann ein Summenglied aus seinem Vorgänger berechnet werden? (Rekursion ?) Seite 22 Rekursion und Iteration Beispiel : Berechnung der Exponentionfunktion / rekursiv Exponentialfunktion x 2 3 e =1 x x /2! x / 3!··· Welche Beziehung besteht zwischen den Summengliedern: ➢ Kann ein Summenglied aus seinem Vorgänger berechnet werden? (Rekursion ?) Startwert =1 1∗x= x x ∗x / 2=x 2 /2 x 2 / 2 ∗x /3=x 3 / 3 ! x 3 / 3! ∗x /4= x 4 / 4 ! · ·· (rekursive) Funktion ? Seite 23 Rekursion und Iteration Beispiel : Berechnung der Exponentionfunktion / rekursiv Exponentialfunktion x 2 3 e =1 x x /2! x / 3!··· Welche Beziehung besteht zwischen den Summengliedern: ➢ Kann ein Summenglied aus seinem Vorgänger berechnet werden? (Rekursion ?) Startwert =1 1∗x= x x ∗x / 2=x 2 / 2 x 2 /2 ∗x / 3= x 3 / 3! x 3 / 3! ∗x / 4=x 4 / 4 ! ·· · s(x, 0) = 1 s(x, i) = s(x, i-1) * x/i Java-Funktion zur rekursiven Berechnung des i-ten Summanden? Seite 24 Rekursion und Iteration Beispiel : Berechnung der Exponentionfunktion / rekursiv Exponentialfunktion x 2 3 e =1 x x /2! x / 3!··· Welche Beziehung besteht zwischen den Summengliedern: ➢ Kann ein Summenglied aus seinem Vorgänger berechnet werden? (Rekursion ?) Startwert =1 1∗x= x x ∗x / 2=x 2 / 2 x 2 /2 ∗x / 3= x 3 / 3! x 3 / 3! ∗x / 4=x 4 / 4 ! ·· · s(x, 0) = 1 s(x, i) = s(x, i-1) * x/i double s(double x, int i) { return i == 0 ? 1 : s(x,i-1) * x / i } e-Funktion ? Seite 25 Rekursion und Iteration Beispiel : Berechnung der Exponentionfunktion / iterativ Exponentialfunktion x 2 3 e =1 x x /2! x / 3!··· Welche Beziehung besteht zwischen den Summengliedern: ➢ Kann ein Summenglied aus seinem Vorgänger berechnet werden? (Iteration ?) Startwert =1 1∗x = x x ∗x /2=x 2 / 2 2 3 x /2 ∗x / 3=x / 3 ! x 3 / 3! ∗x / 4= x 4 / 4 ! · ·· x i x x x x x 0 1 2 3 4 s 1 x x*x/2 x*x*x/2*3 x*x*x*x/2*3*4 Vorwärtsberechnung: Wertverlaufstabelle ··· Funktion zur iterativen Berechnung des i-ten Summanden? Seite 26 Rekursion und Iteration Beispiel : Berechnung der Exponentionfunktion / iterativ Exponentialfunktion x 2 3 e =1 x x /2! x / 3!··· Welche Beziehung besteht zwischen den Summengliedern: ➢ Kann ein Summenglied aus seinem Vorgänger berechnet werden? (Iteration ?) x i x x x x x 0 1 2 3 4 s double s(x,j) { double s = 1; double i = 0; while (i < j) { i = i+1; s = s*x/i; } return s } 1 x x*x/2 x*x*x/2*3 x*x*x*x/2*3*4 ··· e-Funktion ? Seite 27 Rekursion und Iteration Übung Berechnen Sie die Exponentialfunktion e x=1xx 2 /2 ! x3 /3 !· ·· in einer (einzigen) Funktion, die nur eine Schleife verwendet Konstruieren Sie Schleife aus einer Wertverlaufstabelle, in der auch die Summe, d.h. der angestrebte Wert berechnet wird. x i x x x x x 0 1 2 3 4 s 1 x x*x/2 x*x*x/2*3 x*x*x*x/2*3*4 e^x 1 1+x ? ? ? ··· Seite 28 Schleifeninvariante Schleifeninvariante Die Schleifeninvariante ist eine – wesentliche und – zu Beginn jedes Schleifendurchlaufs gültige Aussage – über Variablen die in einer Schleife vorkommen. Die Schleifeninvariante beschreibt – in Form eines Kommentars, – etwas Wichtiges, das sich in der Schleife nicht ändert. Typischerweise handelt es sich um eine Beziehung zwischen Variablenwerten. Seite 29 Schleifeninvariante Beispiel: private static int fakIter(int x) { int n = 0; int f = 1; while (n < x) { // INV f == n! n = n+1; f = f*n; } return f; } Schleifeninvariante: der Wert von f ist immer (genauer: immer vor dem Test der Bedingung) gleich der Fakultät des Wertes von n. Seite 30 Schleifeninvariante Die Schleifeninvariante – Kommentiert Dinge die nicht offensichtlich sind – Bringt die Idee der Schleife zum Ausdruck private static int fakIter(int x) { int n = 0; int f = 1; while (n < x) { // INV f == n! n = n+1; // n wird erhöht f = f*n; // f wird mit n multipliziert } return f; } n ist die Folge der natürlichen Zahlen: offensichtlich erkennbar im Code, Kommentar unsinnig. f wird auf-multipliziert: offensichtlich erkennbar im Code, Kommentar unsinnig. Seite 31 In der Variablen f multipliziere ich die Folge der natürlichen Zahlen auf, die ich in n bilde. Dann ist da immer die Fakultät bis n drin. Wenn dann n = x ist, dann bin ich fertig Schleifeninvariante Die Schleifeninvariante – hilft bei Argumentationen zur Korrektheit private static int fakIter(int x) { int n = 0; int f = 1; while (n < x) { // INV f == n! n = n+1; f = f*n; } // f == n! && !(n < x) // x == n && f == n! => f == x! return f; } Am Ende der Schleife gilt die Invariante und die Negation der Schleifenbedingung. Seite 32 Test Schleifeninvariante – Test mit assert package fakultaet; public class Fakultaet { private static int fak(int x) { if (x == 0) { return 1; } else { return fak(x-1)*x; } } assert: -ea nicht vergessen! private static int fakIter(int x) { int n = 0; int f = 1; assert(f == fak(n)); while (n < x) { // INV f == fak(n) assert(f == fak(n)); n = n+1; f = f*n; } assert(f == fak(x)); return f; } Rekursive Variante als (offensichtlich korrekte) Referenz } Seite 33 Schleifeninvariante Beispiel GGT public static int ggt(int a, int b){ int a0 = a; // „Geistervariable“; Werte van a und b zur Fomulierung int b0 = b; // der Invariante festhalten while (a!=b) { // INV ggt(a0,b0) == ggt(a,b) if (a>b) { a = a - b; } else { b = b -a; } } return a; } Invariante erklärt das Funktionieren des Codes: Die Schleife ist korrekt denn ggt(a,b) = ggt(max(a,b)-min(a,b), min(a,b)) (Mathematik!) Seite 34 Rekursion und Iteration Schleifeninvariante in der Programmentwicklung x 2 3 e =1xx /2 ! x /3 !· ·· x i x x x x x 0 1 2 3 4 s 1 x x*x/2 x*x*x/2*3 x*x*x*x/2*3*4 e 1 1+x 1+x+x*x/2 1+x+x*x/2+x*x*x/2*3 1+x+x*x/2+x*x*x/2*3+ x*x*x*x/2*3*4 ··· Zeilen-Invariante: Welche wichtige Beziehung besteht in jeder Zeile zwischen den Werten der Spalten? Seite 35 Rekursion und Iteration Schleifeninvariante in der Programmentwicklung x 2 3 e =1xx /2 ! x /3 !· ·· x i x x x x x 0 1 2 3 4 s 1 x x*x/2 x*x*x/2*3 x*x*x*x/2*3*4 e public static double eHoch(double x){ double s = 1.0; double e = 1.0; int i = 0; while (i<50) { //INV s=x^i/i!, // e=SUM(j<=i) x^j/j! i= i+1; s = s*x/(double)i; e = e + s; } return e; } Die Idee der 1 Tabelle / Schleife 1+x 1+x+x*x/2 1+x+x*x/2+x*x*x/2*3 1+x+x*x/2+x*x*x/2*3+ x*x*x*x/2*3*4 ··· Zeilen-Invariante: Welche wichtige i s=x /i! Beziehung besteht in jeder Zeile zwischen den Werten der Spalten? Seite 36 i j e=∑ x / j ! j =0