2. Algorithmische Methoden 2.1 Rekursion 18. April 2017
Rekursiver Algorithmus Ein rekursiver Algorithmus löst ein Problem, indem er eine oder mehrere kleinere Instanzen des gleichen Problems löst. Beispiel 2.1: Für n NI ist die Fakultät n! definiert durch { 1, n = 0, n! = n (n 1)!, n 1,
Rekursive Berechnung der Fakultät zu einer natürlichen Zahl n Algorithmus factorial(n) 1 if n = 0 then 2 return 1 3 else 4 return n factorial(n 1) 5 fi
Nicht-Rekursives Programm zur Berechnung der Fakultät zu einer natürlichen Zahl n Algorithmus factorial(n) 1 integer f = 1; 2 for i = 1 to n do 3 f = f i; 4 od 5 return f Bemerkung 2.2: Jedes rekursive Programm kann durch ein nicht-rekursives Programm ersetzt werden, das die gleiche Berechnung durchführt.
Beispiel 2.3: Fibonacci-Zahlen Die Fibonacci-Zahlen sind für n NI wie folgt definiert: 0, n = 0, F n = 1, n = 1, F n 2 +F n 1, n 2.
Programm um die n.te Fibonacci-Zahl zu berechnen Algorithmus Fib(n) 1 if n <= 1 then 2 return n; 3 else 4 return (Fib(n 1)+Fib(n 2)); 5 fi Laufzeit: T(0) = T(1) = 3 T(n) = T(n 1)+T(n 2)+c (falls n > 1), wobei c 3 eine geeignete Konstante ist. Es gilt also insbesondere T(n) F n für n 0.
Übung: Zeigen Sie für alle n 2: ( 1+ 5 F n 2 ) n 2. Daraus folgt, dass die Laufzeit der rekursiven Berechnung exponentiell in n ist; d.h. T(n) = 2 Ω(n).
Berechnungsbaum für F 6 : F 6 F 5 F 4 F 4 F 3 F 3 F 2 F 3 F 2 F 2 F 1 F 2 F 1 F 1 F 0 F 2 F 1 F 1 F 0 F 1 F 0 F 1 F 0 F 1 F 0
Iterative Lösung Algorithmus Fib-Iterative(n) 1 erzeuge Feld F mit n Elementen; 2 F[0] = 0; 3 if n > 0 then 4 F[1] = 1; 5 for i = 2 to n do 6 F[i]=F[i-1]+F[i-2]; 7 od 8 fi 9 return F[n]; Zeitaufwand:O(n)
Beispiel 2.4: Berechnung des ggt(x,y) für x,y 1 Algorithmus ggt(x, y) 1 if x = y then 2 return x; 3 else 4 if x > y then 5 return ggt(x y,y); 6 else 7 return ggt(x,y x) 8 fi 9 fi
Berechnung von ggt(3,6),ggt(4,6) und ggt(4,5): ggt(3,6) erzeugt die folgenden Befehle: ggt(3,3), return 3. ggt(4,6) erzeugt die folgenden Befehle: ggt(4,2), ggt(2,2), return 2. ggt(4,5) erzeugt die folgenden Befehle: ggt(4,1), ggt(3,1), ggt(2,1), ggt(1,1), return 1.
Korrektheit: Die Korrektheit folgt per Induktion über die Anzahl der Rekursionsaufrufe; u.z. zeigen wir für x > y dass die Menge der gemeinsamen Teiler von x und y dieselbe wie die Menge der gemeinsamen Teiler von x y und y ist. Beim Induktionsanfang (Anzahl der Rekursionsaufrufe gleich 0) haben wir x = y, die Menge der Teiler ist gleich und der ggt(x,x) = x. (also korrekte Ausgabe).
Beweis: Behauptung ( ): für x > y gilt: {z : z x z y} = {z : z (x y) z y}. Der Fall x < y geht analog. : z x,z y. Dann gibt es r,s NI, so dass x = z r und y = z s. Daraus folgt: x y = z (r s) und r s > 0 (da x y > 0). Daher gilt z (x y). : z (x y),z y. Dann gibt es t,s NI, so dass (x y) = z t und y = z s. Daraus folgt: x = (x y)+y = z (t +s). Daher gilt z x. Der Induktionsschritt ist also korrekt. Die Mengen in ( ) sind gleich, also auch deren größtes Element (der ggt).
Laufzeit: Die Anzahl der Iterationen (d.h. Anzahl der Rekursionsaufrufe plus 1) ist höchstens x +y 1 und damit ist die Laufzeit O(x +y). Beweis: Die Summen z = x +y (wobei x,y 1 sind) werden stufenweise immer kleiner und sind natürliche Zahlen. z = x +y = z = (x y)+y < z z = x +(y x) = y < z und z,z NI. Bei x = y 1 haben wir keinen weiteren Rekursionsaufruf und der kleinste Summenwert ist 2. Daraus folgt, dass im Worst Case die Anzahl der Iterationen (d.h. Anzahl der Rekursionsaufrufe plus 1) höchstens x +y 1 ist.
Schnellere ggt Berechnung für x,y 1: Algorithmus Euclid(x, y) 1 if y = 0 then 2 return x; 3 else 4 return Euclid(y, x mod y); 5 fi Beispiel Euclid(4, 5) erzeugt folgende Befehle rekursiv: Euclid(5,4 mod 5 = 4), Euclid(4,5 mod 4 = 1), Euclid(1,4 mod 1 = 0), return 1.
Korrektheit: siehe Übung. Vergleiche d = ggt(x,y) mit d = ggt(y,x mod y). Satz 2.5: Der Euklidische Algorithmus berechnet den größten gemeinsamen Teiler von Zahlen x,y NI und x y mit O(log 2 (y)) Operationen.
Beweis von Satz 2.5: Behauptung: Wenn die Berechnung von ggt(x,y) für x,y NI und x > y 1 genau k 1 Rekursionsaufrufe der Prozedur Euclid erfordert, dann gilt x F k+2 und y F k+1 (wobei F i die i.te Fibonacci Zahl ist). Beweis der Behauptung per Induktion über k (siehe Übung). Wegen der Abschätzung ( 1+ 5 ) k 1 y F k+1 2 0.5(k 1). 2 folgt dann 0.5(k 1) log 2 (y) bzw. k 2log 2 y +1 = O(log 2 y). Daher werden insgesamt O(log 2 y) Operationen benötigt.
Folgerung: Der Euklidische Algorithmus berechnet den größten gemeinsamen Teiler von Zahlen x,y NI mit O(log 2 (min(x,y))) Operationen. Beweis der Folgerung: Für x < y wird rekursiv Euclid(y,x mod y) = Euclid(y,x) aufgerufen und die Behauptung folgt aus Satz 2.5.
Beispiel 2.6: Türme von Hanoi Es gebe drei Positionen. Auf der ersten Position liegt anfangs eine Anzahl von Scheiben, die der Größe nach sortiert sind. (Die größte Scheibe liegt unten.) Ziel ist es, den Scheibenstapel von Position 1 nach Position 2 zu verschieben. Zu jedem Zeitpunkt darf nur eine Scheibe von einer Position zu einer anderen verschoben werden und es darf nie eine größere auf eine kleinere Scheibe gelegt werden.
Idee um n Scheiben von Position 1 nach Position 2 zu bewegen: 1. Bewege n 1 Scheiben von Position 1 nach Position 3 (über Position 2) 2. Bewege die größte Scheibe von Position 1 nach Position 2 3. Bewege n 1 Scheiben von Position 3 nach Position 2 (über Position 1)
Programm um n Scheiben von einer Position from zu einer Position to über Position via zu bewegen Algorithmus Hanoi(n, from, to, via) 1 if n > 0 then 2 Hanoi(n-1,from, via, to); 3 Schreibe: Bewege Scheibe von Position from nach to; 4 Hanoi(n-1, via, to, from); 5 fi Befehle für n = 3: Hanoi(3,1,2,3) ruft folgende Befehle auf bzw. schreibt: Hanoi(2,1,3,2) Bewege Scheibe von Position 1 nach 2 Hanoi(2,3,2,1)
Alle Rekursionsaufrufe für n = 3: Hanoi(3,1,2,3) erzeugt: Hanoi(2,1,3,2) erzeugt Hanoi(1,1,2,3) Bewege Scheibe von Position 1 nach 2 Bewege Scheibe von Position 1 nach 3 Hanoi(1,2,3,1) Bewege Scheibe vob Position 2 nach 3 Bewege Scheibe von Position 1 nach 2 Hanoi(2,3,2,1) erzeugt Hanoi(1,3,1,2) Bewege Scheibe von Position 3 nach 1 Bewege Scheibe von Position 3 nach 2 Hanoi(1,1,2,3) Bewege Scheibe von Position 1 nach 2
Graphischer Ablauf für n = 3: 1 nach 2 1 2 3 3 nach 1 1 2 3 1 nach 3 1 2 3 3 nach 2 1 2 3 2 nach 3 1 2 3 1 nach 2 1 2 3 1 nach 2 1 2 3 fertig! 1 2 3
Definition 2.7: Anzahl der Bewegungen Es sei B(n) die Anzahl der Bewegungen für ein Problem mit n Scheiben. Es gilt: B(n) = 2B(n 1)+1 falls n > 1 und B(1) = 1 Lemma 2.8: Der Algorithmus benötigt für n Schreiben B(n) = 2 n 1 Züge.
Beweis: IA n = 1. Hier gilt B(1) = 1 = 2 1 1. IV Es gelte B(n) = 2 n 1 für ein festes n NI. IS Betrachte n+1 und zeige B(n+1) = 2 n+1 1. Wegen der Rekurrenzgleichung gilt B(n+1) = 2B(n)+1 und wegen der IV für n gilt B(n+1) = 2B(n)+1 = 2(2 n 1)+1 = 2 n+1 2+1 = 2 n+1 1.