Induktion – Rekursion

 

Die Rekursion – der Selbstaufruf von Prozeduren – ist eine der großen Errungenschaften der Programmiertechnik. In der Mathematik ist dieses Prinzip schon lange als Induktion bekannt. Aber bis in die sechziger Jahre wurde es als Programmierparadigma noch nicht verwandt. Dabei ist es einer der elegantesten Formulierungskalküle, die die Informatik kennt.

Die Kunst des Programmierens besteht unter anderem darin, eine Aufgabe in sich ständig wiederholende Teilaufgaben zu zerlegen. Erst das Prinzip der Schleife, der gesteuerten Wiederholung macht die Zahl der geschriebenen Statements wesentlich kürzer als die Zahl der ausgeführten Schritte und damit ein Programm wirklich potent.

Wir kennen die for- und die while-Schleife. Die Programmierung, die mit dieser Art Schleifen auskommt, nennen wir iterative Programmierung. Ein Schleife anderer Art liefert der Selbstaufruf von Prozeduren. Dies führt zur rekursiven Programmierung. Es wird nicht ein Block immer wieder durchlaufen (Iteration), sondern ein Block wird in sich selbst verschachtelt (Rekursion).

Dies führt zu einer erstaunlichen Eleganz der Beschreibung eines Problems: das Problem wird in Teilprobleme gleicher Art zerlegt. Das setzt Selbstähnlichkeit voraus: das Problem ähnelt seinen Teilproblemen. Wenn dies der Fall ist, kann das Problem beschrieben werden, indem man seine Zerlegung beschreibt, was in der Regel eine sehr kurze Beschreibung ist.

Der andere Vorteil ist, dass der Rechenaufwand der rekursiven Implementierung eines Problems durch eine Rekursionsgleichung beschrieben und in der Regel einfach errechnet werden kann.

Ein Nachteil ist, dass diese Art der Schleifenbildung ineffiziente Programme liefern kann, wenn dieselben Rechnungen mehrfach gerechnet werden. Dann liegt der Vorteil in der Eleganz und Lesbarkeit des Programms und nicht in seiner Effizienz, d.h. seinem geringen Rechenaufwand.

Beispiel

Wir wollen ein Programm für die Funktion 2n schreiben. Wir wissen  2n = 2 *2n-1   für n > 0 und 20 = 1.

1.     Möglichkeit:

x = 1;
for i = 1 to n do x = 2x.

2.     Möglichkeit:

x = 1;
i = 1;
while i
<= n do begin x = 2x; i = i+1 end.

3.     Möglichkeit:

function exp(n: integer): integer;
begin

    if n = 0 then result:= 1

             else result:= exp(n-1) * 2
end.

 

 

 

Beispiel  23

1. Aufruf n=3

   2. Aufruf n=2

      3. Aufruf n=1

         4. Aufruf n=0

         result = 1

      result = 2

   result = 4

result = 8

 

exp(0) = 1

exp(1) = exp(0)*2

exp(2) = exp(1)*2

exp(3) = exp(2)*2

Wie programmiert man rekursiv.

Zunächst muß die Aufgabe so präzisiert werden, dass erkennbar ist, in welche selbstähnlichen Teilaufgaben sie zerfällt. Dann erstellt man eine formale induktive Definition, die man direkt in ein rekursives Programm übersetzen kann. Schließlich macht man sich anhand einer Beispieleingabe klar, ob die Aufrufe der Prozedur wie gedacht ablaufen.

Beispiel   2n

1.     Präzisierung: 2n = 2 * 2*...*2 (n-mal) und 20 = 1.

2.     induktive Definition: 2n = 1 falls n = 0 und 2n = 2 * 2n-1 sonst.

3.     rekursives Programm: wie oben.

4.     Test: wie oben.

 

Beispiel      n!

1.     Präzisierung: n! = 1 * 2 * 3 ... * n und 0! = 1.

2.     induktive Definition: n! = 1 falls n = 0 und n! = (n - 1)! * n sonst.

3.    rekursives Programm:

function fac (n : integer) : integer

    begin

        if n = 0 then result:= 1

        else result:= fac(n-1)* n

   end;

4.     Test: für n = 3

 

1. Aufruf n=3

   2. Aufruf n=2

      3. Aufruf n=1

         4. Aufruf n=0

         result = 1

      result = 1

   result = 2

result = 6

 

fac(0) = 1

fac(1) = fac(0)*1

fac(2) = fac(1)*2

fac(3) = fac(2)*6

 

 

Beispiel     ggT(m, n) (größter gemeinsamer Teiler von m und n)

1. Präzisierung: ggT(m, n) = größte Zahl, die m und n ohne Rest teilt.

2. induktive Definition: ggT(m, n) = m, falls n = 0, und ggT(m, n) = ggT (n, m mod n), falls n > 0.

3. rekursives Programm:

function ggT(m,n: integer): integer;

    begin

        if n = 0 then result := m
                 else result := ggT(n, m mod n);
    end;

4. Test:    ggT (15, 12)


1. Aufruf  m = 15, n = 12

           2. Aufruf m = 12, n = 3

                     3. Aufruf n = 3, n = 0

                              result = 3

                     result = 3

           result = 3