Click here to load reader

1. Rekursive Prozeduren und Funktionen 2. „Teilen-und ... · 1 Teil VI : Rekursive Algorithmen 1. Rekursive Prozeduren und Funktionen 2. „Teilen-und-Herrschen“ 3. Türme von

  • View
    212

  • Download
    0

Embed Size (px)

Text of 1. Rekursive Prozeduren und Funktionen 2. „Teilen-und ... · 1 Teil VI : Rekursive Algorithmen 1....

  • 1

    Teil VI : Rekursive Algorithmen

    1. Rekursive Prozeduren und Funktionen

    2. „Teilen-und-Herrschen“

    3. Türme von Hanoi

    Zusammenfassung

    Klaus Murmann, Heiko Neumann & Helmuth Partsch, Fakultät für Informatik, Universität Ulm, 2002/03

    RekursionRekursion

    RekursionRekursion

    RekursionRekursion

  • 2

    1. Rekursive Prozeduren und Funktionen

    • Definition rekursiver Funktionen• Struktur rekursiver Algorithmen• Verschiedene Rekursionsarten• Termination rekursiver Funktionen/Prozeduren

    Einordnung

    § Bisher bereits : Rekursive Definition von Syntaxdiagrammen !

    Definition rekursiver Funktionen

    § Neu : Eine Prozedur, die sich selbst aufruft – möglicherweise indirekt über andere Prozeduren – heißt rekursiv

    .9|8|7|6|5|4|3|2|1|0

    .|

    .|

    .|

    −+→

    Ziffer

    Vorzeichen

    ZahlZifferZifferZahl

    ZahlVorzeichenZahlGanzeZahl

    BNF : Syntax für ganze Dezimalzahlen

  • 3

    Struktur

    1. Rekursivität direkt

    PROCEDURE alpha;

    BEGIN

    :

    alpha;

    :

    END alpha;

    PROCEDURE cesar;

    BEGIN

    :

    beta;

    :

    END cesar;

    PROCEDURE beta;

    BEGIN

    :

    cesar;

    :

    END beta;

    2. Rekursivität indirekt

    Beispiel 1 – Fakultät-Funktion

    Definition

    oder

    ( )! 1:!1:!0

    −⋅=

    =

    nnn 1, ≥n

    ( )( )

    >

    =

    −⋅=

    0,

    0,

    1

    1

    n

    n

    nfactnnfact

    Berechnung der Fakultät (rekursiver Algorithmus)

    PROCEDURE fact(n : INTEGER) : INTEGER;

    BEGIN

    IF n = 0 THEN

    RETURN 1

    ELSE

    RETURN n * fact (n–1)

    END

    END fact;

  • 4

    Ergebnis

    Berechnung von 4! – Phasen eines rekursiven Programms

    ( ) ( )( )( )

    ( )( )( )( )( )( )( )

    ( )( )( )( )( )

    ( )

    24

    64

    234

    1234

    11234

    01234

    1234

    234

    344

    =

    ⋅=

    ⋅⋅=

    ⋅⋅⋅=

    ⋅⋅⋅⋅=

    ⋅⋅⋅⋅=

    ⋅⋅⋅=

    ⋅⋅=

    ⋅=

    fact

    fact

    fact

    factfactAbstieg

    Aufstieg

    Ende

    n = 4, n = 3

    n = 2

    n = 1

    n = 0

    Intuitive Erkenntnisse

    § Während des Berechnungsablaufs wird die Funktion fact immer wieder (rekursiv) mit jeweils kleinerem Argument (Parameter) aufgerufen

    § Dabei werden verschiedene Inkarnationen erzeugt (Abstieg) – die Anzahl (hier: 5) hängt vom Algorithmus und vom Parameterwert ab.

    § Das Ende wird durch die Abbruchbedingung

    fact(0) = 1

    bedingt

    § Während des Aufstiegs werden die Zwischenergebnisse verknüpft und die Inkarnationen (ab-)geschlossen und wieder zerstört

  • 5

    Verarbeitung rekursiver Prozeduren mittels Formular-Maschine

    Schritte :

    (1) Erstelle ein Formular für das rekursive Problem

    (2) Übertrage die Argumentwerte in das Formular

    (3) Werte die Bedingung für Fallunterscheidung aus

    (4) Wähle den Zweig der Fallunterscheidung

    (5) Werte nur diesen Zweig aus

    (6) Bei (nicht direkt auswertbaren) rekursiven Aufrufen: neues Formular über das aktuelle legen; weiter bei (2)

    (7) Ist ein Formular vollständig ausgefüllt, so ist dieses zugleich wegzuwerfen und dessen Ergebnis auf das ggf. darunter l iegende Formular zu übertragen; weiter bei (5)

    Ausnahme (= Terminierung) : Das vollständig ausgefüllte Formular ist das letzte; eine Ergebnis-Übertragung ist dann nicht (mehr) möglich ⇒ das Ergebnis ist das gesuchte Endergebnis !

    Schritt (1) : Erstelle ein Formular für das rekursive Problem (hier: Fakultät)

    Formular für n! = fact(n)

    1

    0

    1

    n

    -

    fact(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

  • 6

    Inkarnation 1; n = 4

    1

    0

    1

    n

    -

    fact(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    Schritte (2) – (5)

    4

    44

    4

    3

    false

    ?

    hier kommen wir nicht weiter, d.h., wir „lagern“ dieses Formular jetzt „im Keller“, indem wir ein weiteres fact-Prozedur-Formular darüber le-gen, um die Rechnung fortzusetzen,

    diesmal eines für n = 3 bzw. fact(3)

    Abstieg

    X

    Schritte (6), (2) – (5)

    Inkarnation 1; n = 4

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    4

    44

    4

    3

    false

    ?

    Inkarnation 2; n = 3

    1

    0

    1

    n

    -

    fact(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    2

    false

    ?

    3

    33

    3

    Abstieg

    X

  • 7

    Inkarnation 1; n = 4

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    4

    44

    4

    3

    false

    ?

    Inkarnation 2; n = 3

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    Inkarnation 3; n = 2

    1

    0

    1

    n

    -

    fact(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    1

    false

    ?

    2

    22

    2

    Abstieg

    Schritte (6), (2) – (5)

    X

    Inkarnation 1; n = 4

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    4

    44

    4

    3

    false

    ?

    Inkarnation 2; n = 3

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    Inkarnation 3; n = 2

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    Inkarnation 4; n = 1

    1

    0

    1

    n

    -

    fact(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    0

    false

    ?

    1

    11

    1

    Abstieg

    X

    Schritte (6), (2) – (5)

  • 8

    Inkarnation 1; n = 4

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    4

    44

    4

    3

    false

    ?

    Inkarnation 2; n = 3

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    Inkarnation 3; n = 2

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    Inkarnation 4; n = 1

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    Inkarnation 5; n = 0

    1

    0

    1

    n

    -

    fact(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    true

    11

    0

    00

    0

    Abstieg

    X

    Schritte (6), (2) – (5)

    Inkarnation 1; n = 4

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    4

    44

    4

    3

    false

    ?

    Inkarnation 2; n = 3

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    Inkarnation 3; n = 2

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    Inkarnation 4; n = 1

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    Inkarnation 5; n = 0

    1

    0

    1

    n

    -

    fact(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    -1

    true

    11

    0

    00

    0

    Ende

    X

    Ende des Abstiegs …

  • 9

    Inkarnation 1; n = 4

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    4

    44

    4

    3

    false

    ?

    Inkarnation 2; n = 3

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    Inkarnation 3; n = 2

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    Inkarnation 4; n = 1

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    Inkarnation 5; n = 0

    1

    0

    1

    n

    -

    fact(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    -1

    true

    11

    0

    00

    0

    Aufstieg

    X

    Schritt (7)

    Inkarnation 1; n = 4

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    4

    44

    4

    3

    false

    ?

    Inkarnation 2; n = 3

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    Inkarnation 3; n = 2

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    Inkarnation 4; n = 1

    1

    0

    1

    n

    -

    fact(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    1

    11

    1

    0

    false 1

    1

    11

    Aufstieg

    X

    Schritte (5), (7)

  • 10

    Inkarnation 1; n = 4

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    4

    44

    4

    3

    false

    ?

    Inkarnation 2; n = 3

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    Inkarnation 3; n = 2

    1

    0

    1

    n

    -

    fact(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    2

    22

    2

    1

    false 2

    1

    22

    Aufstieg

    X

    Schritte (5), (7)

    Inkarnation 1; n = 4

    1

    0

    1

    n

    -

    fac(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    4

    44

    4

    3

    false

    ?

    Inkarnation 2; n = 3

    1

    0

    1

    n

    -

    fact(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    3

    33

    3

    2

    false 6

    2

    66

    Aufstieg

    X

    Schritte (5), (7)

  • 11

    Inkarnation 1; n = 4

    1

    0

    1

    n

    -

    fact(n-1)nn

    *=n

    fact(n)if

    thenelse≡≡

    4

    44

    4

    3

    false 24

    6

    2424

    Aufstieg

    Gesamtergebnis

    X

    Schritte (5), (7, diesmal Terminierung)

    Beobachtungen

    § Inkarnationen werden „stapel-artig“ (neudeutsch: „stack-artig“, ‚stack‘ = Stapel), auch „keller-artig“ auf- und abgebaut( → vgl. Tellerstapel in der Mensa)

    § (noch) nicht abgeschlossene Inkarnationen benötigen Speicher(-platz) für ihre Parameter und lokale Variable

    Bezeichnungen : „pulsierender Speicher“, Kellerspeicher, ‚stack‘

    Beachte:

    § es gibt Programmiersprachen, die keine Rekursion erlauben, z.B. FORTRAN !

  • 12

    Beispiel 2 – Fibonacci-Zahlen

    Ursprung: Beispiel zur mathematischen Populationsdynamik (→ Biomathematik)

    „Das Weibchen eines Kaninchenpaars wirft von der Vollendung des 2. Lebensmonats an allmonatlich ein neues Kaninchenpaar. Man berechne die Anzahl F(n) der Kaninchenpaare im Monat n, wenn im [nach] Monat 0 genau ein neugeborenes Kaninchenpaar vorhanden ist.“

    (Aufgabe von Leonardo von Pisa (= Fibonacci), ca. 1180 – ca. 1250; aus K. Jacobs. Einführung in die Kombinatorik, 1983)

    Generiert die Folge :

    n

    F(n)

    0

    0

    1

    1

    2

    1

    3

    2

    4

    3

    5

    5

    6

    8

    7

    13

    8

    21

    9

    34

    10

    55

    11

    89

    12

    144

    Rekursion für die Folge F(0), F(1), ...

    Def.: ( )( )( ) ( ) ( )21

    11

    00

    −+−=

    =

    =

    nFnFnF

    F

    F

    2, ≥n

    (manchmal auch = 1, siehe oben !)Anzahl der Kaninchenpaare des letzten Monats

    Anzahl neugeborener Paare = Anzahl Kaninchenpaare,

    die ≥ 2 Monate alt sind

    Berechnung durch rekursiven Algorithmus (hier: fib(n))

    PROCEDURE fib(n : CARDINAL) : CARDINAL;

    BEGIN

    IF n

  • 13

    Struktur :

    7532 4 6n

    F(n) ≡≡ fib(n)

    Fibonacci

    Aufrufe

    Blätter

    13521 3 8

    ...1442 8 24

    ...832 5 13

    ⇒⇒ Programm mit exponentiellem Zeitbedarf !

    Iteratives Programm, das als Funktion (hier: fibonacci(n)) direkt den Fibonacci-Wert bestimmt :

    PROCEDURE fibonacci(n : CARDINAL) : CARDINAL;

    VAR

    i, a, b, fibo : CARDINAL;

    BEGIN

    IF n = 0 THEN

    fibo := 0

    ELSE

    a := 0;

    b := 1;

    FOR i := 1 TO n DO

    a := a + b;

    b := a – b (* ergibt das „alte“ a ! *)

    END;

    fibo := a

    END;

    RETURN fibo

    END fibonacci;

    Schema :Wert_1‘ := Wert_1 + Wert_2

    Wert_2‘ := Wert_1

    neuer Wert (nach Zuweisung)

  • 14

    Ergebnisberechnung :

    0

    a b fibonacci(n)n

    0

    1 101

    2 111

    3 212

    4 323

    5 535

    6 858

    7 13813

    8 211321

    Beispiel-Implementierungen – Vergleich der Laufzeiten

    Rekursive Lösung Iterative Lösung

    Parameterbeispiele : n = 35, 36, 37, ..., 40

  • 15

    Vorbemerkung

    Viele Aufgabenstellungen, Funktions-Definitionen, etc. sind von „Natur“ aus rekursiv , so daß die Formulierung einer zugehörigen rekursiven Prozedur oft die eleganteste und klarste Lösung der Aufgabe darstellt !

    Struktur rekursiver Algorithmen

    PROCEDURE recProc(...);

    :

    BEGIN

    IF < Rekursionsende erreicht > THEN

    < nicht-rekursiver Teil >

    ELSE

    :

    recProc(...); (* ein oder mehrere rekursive Aufrufe *)

    :

    END;

    :

    END recProc;

    Typischer Aufbau einer rekursiven Prozedur

    zur Erinnerung : Berechnung der Fakultät

    PROCEDURE fact(n : INTEGER) : INTEGER;

    BEGIN

    IF n = 0 THEN

    RETURN 1

    ELSE

    RETURN n * fact(n–1)

    END

    END fact;

    Terminations-Bedingung – Rekursionsende

    Rekursionsende : Ergebnis bei Ende des Rekursions-Abstiegs

    • Rekursion – rekursiver Abstieg• Verknüpfung der Teilresultate

  • 16

    Konstruktion rekursiver Algorithmen (allgemeine Hinweise)

    1. Spezifikation der Aufgabe wie bei anderen Vorhaben auch

    „Schnittstellen-Beschreibung“ : Identifikation der Rolle der Parameter

    2. Konstruktion

    a) Anfang Fallunterscheidung zur Bestimmung des Rekursionsendes, in diesem Teil findet kein rekursiver Aufruf statt

    b) Rekursiver Zweig

    „Denkweise“: zu formulierende rekursive Prozedur existiert bereits als „Black Box“ (wie ein Bibliotheks-Programm)

    Wirkung eindeutig durch Schnittstellen-Beschreibung gegeben !

    Black Box

    Konstruktion rekursiver Algorithmen

    Beispiel : Das Jeep-Problem (1)

    § Gegeben

    • Tankkapazität des Jeeps: c (in dm3)

    • Treibstoff-Verbrauch des Jeeps: v (in dm3/ 100km)

    • Anzahl der Tankfüllungen am Ausgangspunkt: k

    § Gesucht

    Maximal zurücklegbare Entfernung durch geschicktes Anlegen von Zwischen-depots,

    dabei ist die Entfernung zwischen benachbarten Depots so zu wählen, dass genau 1 Tankfüllung verbraucht wird, um den gesamten Treibstoff zu transportieren.

    Der Treibstoff kann aus Sicherheitsgründen nur im (Original-) Tank des Jeeps transportiert werden; für die Depots seien geeignete Lagerbehälter vorhanden.

  • 17

    § Lösungsstrategie

    • von jedem Depot aus jeweils ein neues Tanklager anlegen

    • Entfernung des Depots so bestimmen, dass eine Tankfüllung reicht, um das restliche Benzin in das neue Depot zu schaffen

    • im neuen Depot alles Benzin – bis ggf. auf den Bedarf für die Rückfahrt in das vorherige Depot – aus dem Tank in den Depot-Lagerbehälter ausladen

    Depot-Nr:

    Start

    0 1 2 3

    Tankladungen: k k-1 k-2 k-3 0

    maximale Entfernung maxent(k,v,c) mit k Tankfüllungen

    maximale Entfernung maxent(k-1,v,c) mit k-1 Tankfüllungen

    Distanz dist(k-2,v,c) zwischen Depot 2 und 3

    Ziel

    k

    § Entwicklung der Lösung

    • maxent(i,v,c) ist die maximale Reichweite, die der Jeep mit i (i ≥ 1) Tank-füllungen, Verbrauch v und Tankkapazität c zurücklegen kann

    • offensichtlich gilt:maximale Reichweite mit 1 Tankfüllung: maxent(1,v,c) = 100 · c/v

    • die Strecke dist(i,v,c) zwischen zwei Depots A und B wird wie folgt befahren:

    Hinweg A →→ B : i malRückweg B →→ A : i - 1 mal

    insgesamt also 2i - 1 mal und es gilt :

    • mit diesem dist(i,v,c) ergibt sich als Rekursionsbeziehung:

    • Auslademenge aus(i,v,c) = c – {2 mal Verbrauch auf dist(i,v,c)}

    v1)(2i

    100c)dist(i,v,c

    ⋅−

    ⋅=

    1,v,c)maxent(i)dist(i,v,c,c)maxent(i,v −+=

    1002

    v)dist(i,v,ccaus(i,v,c) ⋅⋅−=

  • 18

    1. Repetitive oder iterative Rekursion („tail recursion“)

    § Der rekursive Aufruf ist die (zeitlich) letzte Anweisung in der Prozedur

    § (Bereits) die Terminierung liefert das Gesamtergebnis; d.h. de facto findet nur ein Abstieg statt. Der Aufstieg besteht lediglich in der Schließung der Inkarnationen

    § Beispiel : Variante der Fakultät : fact(n,m) = fact(n-1,m·n); fact(0,m) = m

    Verschiedene Rekursionsarten

    PROCEDURE fact(

    n,m : CARDINAL): CARDINAL;

    BEGIN

    IF n = 0 THEN

    RETURN m

    ELSE

    RETURN fact(n–1, m*n)

    END

    END fact;

    Formular für fact(n, m)

    0

    m

    *

    fact(n-1,m·n)

    n

    =n

    if

    thenelse≡≡

    fact(n,m)

    n

    m

    1n

    -

    m

    Spezialfall : fact(n,1) = n!

    Bemerkungen

    § Hier werden die noch nicht beendeten Inkarnationen eigentlich nicht gebraucht, da beim Aufstieg aus der Rekursion nur noch Ergebnisübertragungenstattfinden ⇒⇒ effizientere Lösung : Formular überschreiben

    • Man benötigt nur ein Formular und einen Radiergummi

    • Iterative Lösung mit WHILE-Schleife

    • Speicherersparnis der iterativen Lösung gegenüber der rekursiven (nur einFormular!)

    Formular für fact(n,m)

    0

    m

    *

    fact(n-1,m·n)

    n

    =n

    if

    thenelse≡≡

    fact(n,m)

    n

    m

    1n

    -

    m

    Beispiel: fact(4,1) =

    fact(3,4) =

    fact(2,12) =

    fact(1,24) =

    fact(0,24) = 24

    Formular für fact(n,m)

    0

    m

    *

    fact(n-1,m·n)

    n

    =n

    if

    thenelse≡≡

    fact(n,m)

    n

    m

    1n

    -

    m4 1

    4

    4 41

    false

    X1

    3 4

    ?

    Formular für fact(n,m)

    0

    m

    *

    fact(n-1,m·n)

    n

    =n

    if

    thenelse≡≡

    fact(n,m)

    n

    m

    1n

    -

    m3 4

    3

    3 34

    false

    X4

    2 12

    ?

    Formular für fact(n,m)

    0

    m

    *

    fact(n-1,m·n)

    n

    =n

    if

    thenelse≡≡

    fact(n,m)

    n

    m

    1n

    -

    m2 12

    2

    2 212

    false

    X12

    1 24

    ?

    Formular für fact(n,m)

    0

    m

    *

    fact(n-1,m·n)

    n

    =n

    if

    thenelse≡≡

    fact(n,m)

    n

    m

    1n

    -

    m1 24

    1

    1 124

    false

    X24

    0 24

    ?

    Formular für fact(n,m)

    0

    m

    *

    fact(n-1,m·n)

    n

    =n

    if

    thenelse≡≡

    fact(n,m)

    n

    m

    1n

    -

    m0 24

    0

    0 024

    true

    X24

    2424

  • 19

    Iterative Lösung für fact(n, m)

    PROCEDURE fact(n,m : CARDINAL) : CARDINAL;

    VAR

    vn, vm : CARDINAL;

    BEGIN

    vn := n;

    vm := m;

    WHILE vn 0 DO

    vm := vm * vn;

    vn := vn - 1

    END;

    RETURN vm

    END fact;

    2. Lineare Rekursion

    § jeder rekursive Aufruf führt zu höchstens einem weiteren Aufruf(während des Abstiegs)

    § das Resultat eines Aufrufs ist Operand einer umfassenden Operation(während des Aufstiegs)

    § Beispiel : Fakultät (Hinweis: Fibonacci ist kein Beispiel für eine lineare Rekursion !)

    Ergebnis

    ( ) ( )( )( )

    ( )( )( )( )( )( )( )

    ( )( )( )( )( )

    ( )

    24

    64

    234

    1234

    11234

    01234

    1234

    234

    344

    =

    ⋅=

    ⋅⋅=

    ⋅⋅⋅=

    ⋅⋅⋅⋅=

    ⋅⋅⋅⋅=

    ⋅⋅⋅=

    ⋅⋅=

    ⋅=

    fact

    fact

    fact

    factfactAbstieg

    Aufstieg

    Ende

    n = 4, n = 3

    n = 2

    n = 1

    n = 0

    Jeweils ein Aufrufpro Rekursions-stufe während des Abstiegs

    Operation während des Aufstiegs ist die Multiplikation

  • 20

    3. Baumartige Rekursion

    § Jeder nichtterminierende rekursive Aufruf führt zu mindestens zweiweiteren Aufrufen (während des Abstiegs)

    § Beispiel : Binomialkoeffizient(Hinweis: Fibonacci ist auch ein Beispiel für eine baumartige Rekursion !)

    n Werte der Binomialkoeffizienten(Pascal‘sches Dreieck)

    1

    11

    1 12

    4

    1

    1

    13 3

    6 4 1

    105 10 5 11

    2015 15 6 161

    ...

    0

    1

    6

    2

    3

    4

    5

    :

    k = 0 →→ n

    k

    n

    10

    =

    n1=

    n

    n

    nkkfallsk

    n

    k

    n

    k

    n

    −+

    −=

    0

    1

    1

    1

    10515

    2

    5

    1

    5

    2

    6:

    +=

    +

    =

    Beispiel

    Aufrufbaum für , 10

    =

    n1=

    n

    n, 0

    1

    1

    1nkkfalls

    k

    n

    k

    n

    k

    n

    −+

    −=

    am Beispiel n = 4, k = 2 :

    bin(4,2)

    bin(3,2)bin(3,1)

    bin(2,2)bin(2,1)bin(2,1)bin(2,0)

    bin(1,1)bin(1,0)bin(1,1)bin(1,0)

    = 1

    = 1 = 1 = 1 = 1

    = 1

    PROCEDURE bin(

    n,k : CARDINAL) : CARDINAL;

    BEGIN

    IF (k = 0) OR (k = n)

    THEN RETURN 1

    ELSE bin(n-1,k-1) + bin(n-1,k)

    END

    END bin;

    Summation : bin(4, 2) = 6

  • 21

    4. Geschachtelte Rekursion

    § Argument eines rekursiven Aufrufs ist selbst rekursiver Aufruf

    § Beispiel : Modulo

    5. Verschränkte Rekursion

    § Prozedur f ruft Prozedur g und sich selbst auf

    § Prozedur g ruft Prozedur f und sich selbst auf

    Bemerkungen zu 4. und 5.

    § Auch diese Arten sind nicht ohne Weiteres mittels einfacher Schleifen zu ersetzen

    § Hinweis : Beliebige Rekursionsarten lassen sich immer dadurch in eine iterativeForm bringen, dass die „kellerartige“ Verarbeitung der Rekursion ausprogrammiert

    wird.

    PROCEDURE modulo(a,b: CARDINAL): CARDINAL;

    BEGIN

    IF a < b THEN RETURN a

    ELSIF a < 2b THEN RETURN a-b

    ELSE RETURN modulo(modulo(a,2*b),b)

    END modulo;

    Termination rekursiver Funktionen / Prozeduren

    § Intuitiv

    zu zeigen:

    § jeder rekursive Aufruf führt zu „kleineren“ Argumenten§ nach einer endlichen Anzahl von Aufrufen wird Terminationsargument erreicht

    § formal

    f inde eine (streng monoton fallende) Abbildung

    t: „Parameterbereich“ →→ N0mit

    t(Argument im rekursiven Aufruf) < t(formalen Argument)

    für alle rekursiven Aufrufe.

    bin(n,k) terminiert

    § Beispiel : Binomialkoeffizient bin(n,k)

    wähle: t: N0 x N0 →→ N0 als t(n,k) = n + k

    dann: t(n-1,k-1) = n+k-2 < n+k = t(n,k)t(n-1,k) = n+k-1 < n+k = t(n,k)

  • 22

    2. „Teilen-und-Herrschen“

    • Prinzip „Teilen-und-Herrschen“ (“Divide-and-conquer”)

    • Beispiel – Markieren eines Lineals

    Konzept

    Prinzip „Teilen-und-Herrschen“ („Divide-and-conquer“)

    Eigenschaften

    § Lösungen (Algorithmen) nach dem „Teile-und-Herrsche“-Prinzip sind baumartige Rekursionen und lassen sich daher i.a. nicht direkt in iterative Verfahren abbilden !

    § Bei „Teilen-und-Herrschen“-Verfahren finden meist keine redundanten Berechnungen statt, da die Eingabedatenmenge in nicht-überlappende Teilmengen zerlegt wird :

    ( ) ( ) ( )∅=∩

    +=∪

    BA

    BcardAcardBAcard oder

    Häufig bieten sich rekursive Lösungen für ein Problem an, in denen die

    Eingabemenge in 2 (etwa gleich große) Teile zerlegt wird, die jeweils

    durch rekursive Aufrufe des Lösungs-Algorithmus bearbeitet werden !

    Je nach Aufgabenstellung kann die Zerlegung auch in mehr als 2 Teile

    erfolgen !

    siehe

    Binomial-koeffizient

    oder

    Fibonacci

  • 23

    Beispiel – Markieren eines Lineals

    0 1

    Markierung bei

    Markierung bei und

    Markierung bei und und und

    [ ]321

    2/1

    1:02

    1

    434214/1

    2

    1:0

    2

    1

    434214/3

    1:2

    1

    2

    1

    434218/1

    4

    1:0

    2

    1

    434218/3

    2

    1:

    4

    1

    2

    1

    434218/5

    4

    3:

    2

    1

    2

    1

    434218/7

    1:4

    3

    2

    1

    Aufgabe :

    Strategie : mehrstufige Intervallhalbierung

    Strategie der rekursiven Lösung

    1. Maximale Markierung festlegen

    2. Das Lineal wird sukzessive halbiert

    3. Die Mitte wird markiert

    (Länge der Markierung = Anzahl der letzt-gröberen Markierung - 1)

    4. Für jede (aktuelle) Lineal-Hälfte werden die Schritte 2. und 3.

    Halbierung + Markierung

    wiederholt

    5. Ende : Länge der Markierung = 0 oder gleichbedeutendLänge des aktuellen Teil-Lineals = 0

    Algorithmus – Entwurfsentscheidungen

    1. Das Lineal ist als l ineares Feld aus INTEGER / CARDINAL-Elementen realisiert

    2. Die Werte der Elemente (des Feldes) bezeichnen die Längen der Markierungen

    Initialisierung (angenommen) : ( ) [ ] 0 :10, =−≤≤∀ iANii

  • 24

    :

    CONST

    N = < cardinal >;

    VAR

    ruler : ARRAY [0..N] OF CARDINAL;

    begin, end, mheight : CARDINAL;

    PROCEDURE mark(index, height : CARDINAL);

    BEGIN

    ruler[index] := height

    END mark;

    PROCEDURE rule(i_begin, i_end, mheight : CARDINAL);

    VAR

    i_middle : CARDINAL;

    BEGIN

    IF mheight > 0 AND (i_end – i_begin) > 0 THEN

    i_middle := (i_begin + i_end) DIV 2;

    mark(i_middle, mheight);

    rule(i_begin, i_middle, mheight-1);

    rule(i_middle, i_end, mheight-1)

    END

    END rule;

    Rekursionsende hier mit „leerer“ Aktion !

    Zahl, z.B. 64

    BEGIN (* -- Hauptprogramm *)

    :

    mheight := ...;

    begin := 0;

    end := N;

    rule(begin, end, mheight)

    END ...

    3. Türme von Hanoi

    • Problem• Strategie• Rekursiver Algorithmus• Formaler Beweis der Korrektheit• Aufwandsabschätzungen (rekursive Lösung)• Einfacher nicht-rekursiver Algorithmus

  • 25

    Kupfer GoldSilber

    Nach einer alten Legende standen vor langer Zeit vor einem Tempel in Hanoi drei Säulen :

    • eine aus Kupfer , • eine aus Silber,• eine aus Gold .

    Problem

    4

    1

    2

    3

    Auf der kupfernen Säule befanden sich hundert verschieden große Scheiben aus Porphyr (vulkanisches Gestein), wobei die Scheiben in ihrer Größe nach oben hin immer kleiner wurden :

    Ein alter Mönch hatte sich die Aufgabe gestellt, alle Scheiben von der kupfernenzur goldenen Säule zu tragen. Da die Porphyrscheiben sehr schwer waren, konnte der Mönch immer nur eine Scheibe gleichzeitig transportieren. Da die Säule ihm gleichzeitig als Treppe diente, durfte bei der Umschichtung nie eine größere auf eine kleine Scheibe gelegt werden.

    Wenn der Mönch – so die Legende – seine Aufgabe erfüllt habe, so werde das Ende der Welt kommen.

    Kupfer GoldSilber

    4

    1

    2

    3

  • 26

    Der Mönch bemerkte sehr schnell, daß er beim Transport der Scheiben auch die silberne Säule benötigte , da er ja immer nur eine Scheibe gleichzeitig tragenkonnte. Nach einigen Tagen Meditation bekam er auf einmal die Erleuchtung:

    Strategie

    Beim Betrachten dieses Schemas bemerkte der Mönch, daß Teil 1 und Teil 3 außerordentlich mühsam sein würden. Da er nicht nur ein alter, sondern auch ein weiser Mönch war, entschloß er sich, Teil 1 von seinem ältesten Schüler ausführen zu lassen. Wenn dieser mit der Arbeit fertig wäre, würde der Mönch selbst die große Scheibe von der kupfernen zur goldenen Säule tragen – und dann nochmals die Dienste seines ältesten Schülers in Anspruch nehmen.

    Die Aufgabe kann in drei Teilaufgaben zerlegt werden:

    Teil 1 : Transportiere den Turm – bestehend aus 99 oberen Scheiben von der kupfernen zur silbernen Säule

    Teil 2 : Transportiere die übriggebliebene 100ste Scheibe (ganz unten) von der kupfernen zur goldenen Säule

    Teil 3 : Transportiere den Turm mit den 99 Scheiben von der silbernenzur goldenen Säule

    Kupfer GoldSilber

    4

    1

    2

    3

    Kupfer GoldSilber

    4

    1

    2

    3

    Start (Quelle) ZielSchema :

  • 27

    Kupfer GoldSilber

    4

    1

    2

    3

    Kupfer GoldSilber

    4

    1

    2

    3

    Ziel

    Um seinem ältesten Schüler, der selbst schon in den Jahren war, nicht zu viel Arbeit zu machen, wollte er ihm diesen Plan mitteilen, damit auch er es sich leicht machen könnte.

    Der Algorithmus , den der Mönch am nächsten Tag an die Tempeltür nagelte, ist aus dem Alt-Vietnamesischen übersetzt :

    Anleitung , um einen Turm von n Scheiben von der einen zu der anderen Säule – unter Verwendung einer weiteren (dritten) Säule –zu transportieren :

    Wenn der Turm aus mehr als einer Scheibe besteht,

    • dann bitte Deinen ältesten Schüler, einen Turm von n-1 Scheiben von der ersten zur dritten Säule zu transportieren;

    • trage selbst eine Scheibe von der ersten zur anderen Säule;

    • bitte Deinen ältesten Schüler, einen Turm von n-1 Scheiben von der dritten zur anderen Säule zu transportieren.

  • 28

    Als der Mönch dieses Dokument festgenagelt hatte, fragte er sich, was jetzt zu tun sei – er musste einen Turm von 100 Scheiben von der kupfernen zur goldenen Säule transportieren. Weil er nach der schweren Denkarbeit der vorherigen Tage etwas zerstreut war, wußte er nicht mehr genau, wie er das machen sollte; als er vor der Tempeltür eine Traube von Menschen sah, die offenbar etwas lasen, erinnerte er sich wieder. Entschlossen drängelte er sich zur Tempeltür und las, wie zu verfahren sei.

    Und so rief er seinen ältesten Schüler zu sich und bat ihn, einen Turm von 99 Scheiben von der kupfernen zur silbernen Säule (unter Verwendung der goldenen) zu transportieren – und sich danach wieder bei ihm zu melden ...

    (nach G. Hommel, C.A.H. Koster. Algorithmen, TU Berlin, 1977/78)

    1links

    3rechts

    2mitte

    Grobstruktur

    geg. : Turmhöhe : hoehe (einlesbar)

    Ausgangssäule : 1 (links)

    Zielsäule : 3 (rechts)

    Rekursiver Algorithmus

    Nummerierung

  • 29

    MODULE TuermeVonHanoi;

    FROM InOut IMPORT WriteString,WriteLn,ReadCard;

    TYPE saeule = (1, 2, 3); (* Saeulenpositionen links, mitte, rechts *)

    VAR hoehe : CARDINAL; (* Höhe des Turms = Anzahl der Scheiben *)

    PROCEDURE drucke(x : saeule) (* druckt die Säulenposition aus *)

    BEGIN

    CASE x OF

    1: WriteString(’ links’)|

    2: WriteString(’ mitte’)|

    3: WriteString(’ rechts’)

    END

    END drucke;

    PROCEDURE TurmBewegung(hoehe : CARDINAL; A, B : saeule)

    (* bewegt einen Turm der Höhe ‚hoehe‘ von Säule A nach Säule B *)

    BEGIN

    IF hoehe > 0 THEN

    TurmBewegung(hoehe-1, A, 6-A-B);

    WriteString(’Oberste Scheibe von ’);

    drucke(A); WriteString(’ nach’); drucke(B);

    TurmBewegung(hoehe-1, 6-A-B, B)

    END

    END TurmBewegung;

    BEGIN

    WriteString(’Turmhoehe = ’); ReadCard(hoehe); WriteLn;

    TurmBewegung(hoehe, 1, 3)

    END TuermeVonHanoi.

    Rekursionsende bei hoehe = 0

    Rekursion

    a = Position Ausgangssäuleb = Position Zielsäule6 - a - b = Position Hilfssäule

    „BewegeScheibe(A, B)“

    „Handsimulation“ von TuermeVonHanoi

    1 32123

    hoehe = 3

    TurmBewegung(3, 1, 3)

    hoehe = 3 > 0:

    TurmBewegung(2, 1, 6-1-3=2)

    hoehe = 2 > 0:

    TurmBewegung(1, 1, 6-1-2=3)

    hoehe = 1 > 0:

    TurmBewegung(0, 1, 6-1-3=2)

    hoehe = 0 ⇒⇒ EXIT

    BewegeScheibe(1, 3)

    TurmBewegung(0, 3, 6-1-3=2)

    hoehe = 0 ⇒⇒ EXIT

    BewegeScheibe(1, 2)

    TurmBewegung(1, 6-1-2=3, 2)

    hoehe = 1 > 0:

    TurmBewegung(0, 3, 6-3-2=1)

    hoehe = 0 ⇒⇒ EXIT

    BewegeScheibe(3, 2)

    TurmBewegung(0, 6-3-2=1, 2)

    BewegeScheibe(1, 3)

    TurmBewegung(2, 6-1-3=2, 3)

    : < →→ nächste Seite >

    Start :

    1 32

    231

    1 322 31

    1 3223

    1

    1 3223

    1

  • 30

    1 3212

    3:

    BewegeScheibe(1, 3)

    TurmBewegung(2, 6-1-3=2, 3)

    hoehe = 2 > 0:

    TurmBewegung(1, 2, 6-2-3=1)

    hoehe = 1 > 0:

    TurmBewegung(0, 2, 6-2-1=3)

    hoehe = 0 ⇒⇒ EXIT

    BewegeScheibe(2, 1)

    TurmBewegung(0, 6-2-1=3, 1)

    hoehe = 0 ⇒⇒ EXIT

    BewegeScheibe(2, 3)

    TurmBewegung(1, 6-2-3=1, 3)

    hoehe = 1 > 0:

    TurmBewegung(0, 1, 6-1-3=2)

    hoehe = 0 ⇒⇒ EXIT

    BewegeScheibe(1, 3)

    TurmBewegung(0, 6-3-2=1, 2)

    hoehe = 0 ⇒⇒ EXIT

    TERMINATION

    1 3223 1

    1 32

    23 1

    1 32

    23

    1

    Finale : 1 32

    23

    1Programmende

    Frage : Liefert der Algorithmus stets die korrekte Lösung ?

    → Vollständige Induktion

    Formaler Beweis der Korrektheit

    1 3 2

    B. Induktionsannahme : Der Algorithmus arbeitet für n Scheiben korrekt

    1 3 2

    n

    C. Induktionsschritt : Arbeitet der Algorithmus auch für n + 1 Scheiben korrekt ?

    Bewege n + 1 Scheiben von 1 nach 3 (Hilfssäule ist 2)

    BewegeScheibe(1, 3)

    Bewege 1 Scheibe von 1 nach 3

    A. Induktionsbeginn : n = 1

  • 31

    TurmBewegung(n+1, 1, 3)

    1. hoehe = n+1 > 0:

    2. TurmBewegung(n, 1, 2)

    3. BewegeScheibe(1, 3)

    4. TurmBewegung(n, 2, 3)

    2. gilt laut Induktionsannahme , da hoehe = n und damit

    Bewege n Scheiben von 1 nach 2 (Hilfssäule : 3)

    3. gilt laut Induktionsbeginn , mit

    Bewege 1 Scheibe von 1 nach 3

    4. gilt laut Induktionsannahme , da hoehe = n und damit

    Bewege n Scheiben von 2 nach 3 (Hilfssäule : 1)

    außerdem gilt die Transitivität der Transportoperation BewegeScheibe(., .) :

    BewegeScheibe(1, 2) ∧∧ BewegeScheibe(2, 3) ⇒⇒ BewegeScheibe(1, 3)

    ⇒ Induktionsschluß : Der Algorithmus arbeitet auch für n + 1 Scheiben !

    Frage : Wie oft müssen Scheiben hin- und hergetragen werden ?

    Aufwandsabschätzung (rekursive Lösung)

    Vermutung : Bei Betrachtung der Zahlenfolge 1, 3, 7, 15, ... liegt die Vermutung nahe, dass bei n Scheiben 2n – 1 Trageoperationen notwendig sind !

    Überprüfung : Trageoperationen bei n = 1 + 2·Trageoperationen bei (n – 1)

    ( )

    12

    221

    122112 1

    −=

    −+=

    −⋅+=− −

    n

    n

    nn ?

    ?⇒ Die Anzahl der Trageoperationen

    nimmt exponentiell mit n zu !

    1

    2

    3

    4

    :

    n

    Scheibenanzahl Trageoperationen

    1

    1 + 1 + 1 = 3

    3 + 1 + 3 = 7

    7 + 1 + 7 = 15

    :

    1 + 2·Trageoperationen bei (n-1)

    Ausgangssituation: Auf der kupfernen Säule befinden sich n ≥ 0 Scheiben

  • 32

    Zurück zum Anfang . . .

    . . . das Ende der Welt ist dann wahrscheinlich (in der Tat) nicht mehr fern !

    Wenn der Mönch – so die Legende – seine Aufgabe erfüllt habe, so werde das Ende der Welt kommen.

    )(!104.2

    .min1026765.1.min1224

    30100

    Jahre⋅≈

    ⋅≈− (1 Jahr = 525600 min.)

    Wenn alle Mönche für den Transport der 100 Scheiben sehr fleißig arbeiten und jede Minute eine Scheibe transportieren, dann benötigen sie für den Transport des Turms

    Struktur

    → Es wird keine „Ziel-Säule“ angegeben !

    Einfacher nicht-rekursiver Algorithmus 1

    3 2

    (nach D. Harel. The science of computing. Addison-Wesley, 1989, p.107)

    :

    LOOP

    < bewege die kleinste Scheibe (= oberste Scheibe der Anfangs-Säule)um eine Position „im Uhrzeigersinn“ (also 1 →→ 2 oder 2 →→ 3 oder 3 →→ 1) >;

    IF < alle Scheiben korrekt auf einer anderen Säule (= Zielsäule) gestapelt > THENEXIT

    END; (* IF *)

    < bewege die kleinste Scheibe nicht, mache den einzig möglichen momentan verbleibenden Zug >

    END; (* LOOP *)

    :

  • 33

    2

    1

    3

    Ablauf für einen Turm mit 4 Scheiben

    2

    1

    3 2

    1

    3 2

    1

    3 2

    1

    3 2

    1

    3 2

    1

    3 2

    1

    3 2

    1

    3 2

    1

    3 2

    1

    3 2

    1

    3 2

    1

    3 2

    1

    3 2

    1

    3 2

    1

    3 2

    1

    3 2

    1

    3

    Fertig !

    Ablauf

    Transport einer geraden Anzahl von Scheiben, z.B. 4 – 3 – 2 – 1 , Zielstapel auf nächster Säule im Gegenzeigersinn

    4

    1

    2

    3

    Transport einer ungeraden Anzahl von Scheiben, z.B. 5 – 4 – 3 – 2 – 1 , Zielstapel auf nächster Säule im Uhrzeigersinn

    4

    1

    2

    3

    5

    2

    1

    3

    3 2

    1

    Start

    Zielstapel

    Start

    Zielstapel

  • 34

    § Rekursive Funktionen / Prozeduren rufen sich selbst auf – direkt oder indirekt

    § Die Verarbeitung rekursiver Prozeduren lässt sich anschaulich durch die „Formular-Maschine“ darstellen. Zur Verwaltung der bei jedem Aufruf (Inkarnation) angelegten Variablen und übergebenen Parameter wird (vom System) ein pulsierender Speicher (auch: „Stack“, Kellerspeicher) verwendet

    § Verarbeitungs-Phasen eines rekursiven Programms :• Abstieg• Ende (Terminationsbedingung) • Aufstieg (ggf. mit Zusammenfügen der Teilergebnisse)

    § Es werden verschiedene Rekursionsarten unterschieden: repetitive, lineare, baumartige, geschachtelte, verschränkte Rekursion

    § Die Terminierung rekursiver Funktionen ist ein wesentlicher Aspekt. Sie wird mit einer streng monoton fallenden Abb. t:(Parameterbereich) → N untersucht.

    § Verfahren nach dem Prinzip „Teilen-und-Herrschen“ zerlegen die Datenmenge in etwa gleiche Teile und wenden den Algorithmus jeweils auf den Teilmengen an.

    § Das „Türme-von-Hanoi“-Problem stellt ein klassisches Beispiel dar, eine Aufgabe rekursiv in einfacher Weise zu lösen , eine gleichwertige iterative Lösung ist dagegen sehr viel schwerer durchschaubar; rekursive Lösungen dieser Art benötigen exponentiellen Aufwand

    Zusammenfassung