Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

Grundlagen der Informatik 1 Thema 6: Generative Rekursion

Ähnliche Präsentationen


Präsentation zum Thema: "Grundlagen der Informatik 1 Thema 6: Generative Rekursion"—  Präsentation transkript:

1 Grundlagen der Informatik 1 Thema 6: Generative Rekursion
Prof. Dr. Max Mühlhäuser Dr. Guido Rößling

2 Inhaltsverzeichnis Einführung in generativ rekursive Funktionen
Sortieren mittels generativ rekursiver Prozeduren Richtlinien für den Entwurf von generativ rekursiven Prozeduren Strukturelle versus generative Rekursion Backtracking-Algorithmen: Durchlaufen von Graphen

3 Generative Rekursion Bisher haben wir strukturelle Rekursion verwendet, um strukturell rekursive Daten zu verarbeiten Wir haben die Eingabedaten in ihre direkten strukturellen Komponenten zerlegt Wir haben die Komponenten verarbeitet und die Ergebnisse kombiniert Allerdings: Nicht alle Probleme lassen sich mit strukturell rekursiven Funktionen lösen Auch wenn es geht, ist strukturelle Rekursion nicht immer die beste Lösung In dieser Vorlesung werden wir eine neue Funktionsart kennen lernen Generativ rekursive Funktionen

4 Generative Rekursion Teile und herrsche (Divide & Conquer)
Wenn das Problem trivial lösbar ist, wird die entsprechende Lösung zurückgeliefert Ansonsten: Teile das Problem in neue kleinere Teilprobleme (es werden kleinere Probleme generiert) Herrsche: Die kleineren Probleme werden gelöst Kombiniere die Lösungen der kleineren Probleme zu einer Lösung für das Ursprungsproblem Design von generativ rekursiven Funktionen (Algorithmen) ist eher eine kreative Aktivität, die einen Einblick braucht – ein “Heureka, ich hab's!”.

5 Modellieren eines Balls, der auf dem Tisch rollt…
Aufgabenbeschreibung Der Ball rollt mit einer konstanten Geschwindigkeit, bis er von der Tischkante herabfällt Den Tisch stellen wir als einen Fläche mit einer festgelegten Länge und Breite dar Den Ball stellen wir als eine Scheibe dar, welche sich auf der Fläche bewegt Bewegung stellen wir durch die Wiederholung folgender Schritte dar: Zeichne die Scheibe in der aktuellen Position auf der Fläche Warte eine bestimmte Zeitperiode Lösche die Scheibe von der aktuellen Position Verschiebe sie an die aktuelle Position

6 Ballstruktur und -operationen
;;TeachPack: draw.ss ;; structure: (make-ball number number number number) (define-struct ball (x y delta-x delta-y)) ;; draw-and-clear : a-ball -> true (define (draw-and-clear a-ball) (and (draw-solid-disk (make-posn (ball-x a-ball) (ball-y a-ball)) 5 'red) (sleep-for-a-while DELAY) (clear-solid-disk (make-posn (ball-y a-ball)) 5 'red))) 1. A ball is a structure with four fields: the current position and the velocity in each direction. That is, the first two numbers in a ball structure are the current position on the canvas, and the next two numbers describe how far the ball moves in the two directions per step. 2. The function move-ball models the physical movement of the ball. It consumes a ball and creates a new one, modeling one step. 3. The function draw-and-clear draws the ball at its current position, then waits for a short time, and clears it again.

7 Ballstruktur und -operationen
;; move-ball : ball -> ball (define (move-ball a-ball) (make-ball (+ (ball-x a-ball) (ball-delta-x a-ball)) (+ (ball-y a-ball) (ball-delta-y a-ball)) (ball-delta-x a-ball) (ball-delta-y a-ball))) ;; Dimension of surface (define WIDTH 100) (define HEIGHT 100) ;; Delay constant (define DELAY .1) 1. A ball is a structure with four fields: the current position and the velocity in each direction. That is, the first two numbers in a ball structure are the current position on the canvas, and the next two numbers describe how far the ball moves in the two directions per step. 2. The function move-ball models the physical movement of the ball. It consumes a ball and creates a new one, modeling one step. 3. The function draw-and-clear draws the ball at its current position, then waits for a short time, and clears it again.

8 Das Rollen des Balls Um den Ball einige Male zu verschieben, können wir schreiben: Das wird nach einiger Zeit langweilig. Wir brauchen eine Funktion, die den Ball verschiebt, bis er außerhalb der Grenze ist. (define the-ball (make-ball )) (and (draw-and-clear the-ball) (draw-and-clear (move-ball the-ball)) ...))

9 Das Rollen des Balls Herausfinden, ob ein Ball außerhalb der Grenze ist: Schablone für die Funktion, die den Ball verschiebt, bis er außerhalb der Grenze ist: ;; out-of-bounds? : a-ball -> boolean (define (out-of-bounds? a-ball) (not (and (<= 0 (ball-x a-ball) WIDTH) (<= 0 (ball-y a-ball) HEIGHT)))) Der triviale Fall: wir geben true zurück ;; move-until-out : a-ball -> true (define (move-until-out a-ball) (cond [(out-of-bounds? a-ball) ... ] [else ...])) The easy part is to define out-of-bounds?, a function that determines whether a given ball is still visible on the canvas: In contrast, writing a function that draws the ball on the canvas until it is out of bounds belongs to a group of programs that we haven't encountered thus far. Because the function consumes a ball and draws its movement on a canvas, it produces true like all other functions that draw onto a canvas. Designing it with the recipe for structures makes no sense, however. After all, it is already clear how to draw-and-clear the ball and how to move it, too. What is needed instead is a case distinction that checks whether the ball is out of bounds or not. true ?

10 Das Rollen des Balls Nachdem der Ball gezeichnet und verschoben wurde, wenden wir move-until-out wieder an: rekursive Funktion (define (move-until-out a-ball) (cond [(out-of-bounds? a-ball) true] [else (and (draw-and-clear a-ball) (move-until-out (move-ball a-ball)))])) Wir können nun die Funktion wie folgt testen: Eine Fläche der richtigen Größe, und ein Ball, der sich nach links unten bewegt, werden erzeugt. If the ball consumed by move-until-out is outside of the canvas's boundaries, the function can produce true, following the contract. If the ball is still inside the boundaries, two things must happen. First, the ball must be drawn and cleared from the canvas. Second, the ball must be moved, and then we must do things all over again. This implies that after moving the ball, we apply move-until-out again, which means the function is recursive. A close look at the function definition reveals two peculiarities. First, although the function is recursive, its body consists of a cond-expression whose conditions have nothing to do with the input data. Second, the recursive application in the body does not consume a part of the input. Instead, move-until-out generates an entirely new and different ball structure, which represents the original ball after one step, and uses it for the recursion. Clearly, none of our design recipes could possibly produce such a definition. We have encountered a new way of programming. (start WIDTH HEIGHT) (move-until-out (make-ball )) (stop)

11 Neuer Typ von Rekursion
Die Prozedur move-until-out verwendet einen neuen Typ von Rekursion Bedingungen haben nichts mit den Eingabe-Daten zu tun Die rekursive Anwendung im Rumpf verarbeitet keinen Teil der Eingabe move-until-out generiert eine andere komplett neue Ball-Struktur und benutzt diese für die Rekursion (define (move-until-out a-ball) (cond [(out-of-bounds? a-ball) true] [else (and (draw-and-clear a-ball) (move-until-out (move-ball a-ball)))])) Wir haben noch kein Designrezept dafür If the ball consumed by move-until-out is outside of the canvas's boundaries, the function can produce true, following the contract. If the ball is still inside the boundaries, two things must happen. First, the ball must be drawn and cleared from the canvas. Second, the ball must be moved, and then we must do things all over again. This implies that after moving the ball, we apply move-until-out again, which means the function is recursive. A close look at the function definition reveals two peculiarities. First, although the function is recursive, its body consists of a cond-expression whose conditions have nothing to do with the input data. Second, the recursive application in the body does not consume a part of the input. Instead, move-until-out generates an entirely new and different ball structure, which represents the original ball after one step, and uses it for the recursion. Clearly, none of our design recipes could possibly produce such a definition. We have encountered a new way of programming.

12 Inhaltsverzeichnis Einführung in generativ rekursive Funktionen
Sortieren mittels generativ rekursiver Prozeduren Richtlinien für den Entwurf von generativ rekursiven Prozeduren Strukturelle versus generative Rekursion Backtracking: Durchlaufen von Graphen

13 Sortieren: Quicksort & Mergesort
Es geht erneut um das Sortieren der Elemente einer Liste… Wir haben schon insertion sort kennengelernt Eine strukturell rekursive Prozedur Nun werden wir zwei andere Algorithmen zum Sortieren kennen lernen: Quicksort & Mergesort Klassische Beispiele für generative Rekursion Basieren auf der "Teile & Herrsche"-Idee

14 [Erinnerung: insertion sort]
;; sort : list-of-numbers  ->  list-of-numbers ;; creates a sorted list of numb. from numbers in alon (define (insertion-sort alon) (cond [(empty? alon) empty] [else (insert (first alon) (sort (insertion-sort rest alon)))])) unsorted sorted an an

15 Quicksort: Die Idee Der Verlauf eines beliebigen Zwischenschritts: das Sortieren einer beliebigen Subliste L0 =(list elp…elr) Teile: Partitioniere L0 in zwei (eventuell leere) Listen, L1 = (list elp…elq-1) und L2 = (list elq+1…elr), so dass: jedes Element aus L1 kleiner oder gleich elq ist, jedes Element aus L2 größer als elq ist Herrsche: Wende die gleiche Prozedur rekursiv an, um L1 und L2 zu sortieren Kombiniere: Stelle die Elemente der sortierten Listen L1 und L2 einfach nebeneinander Drehpunkt elq <= elq > elq

16 Quicksort: Die Idee Zwei offene Fragen bisher:
Wie wählen wir das Drehpunktelement? Wir nehmen das erste Element als Drehpunkt Wann hören wir auf? Mit anderen Worten: Was ist der Trivialfall für Quicksort? Die leere Liste ist immer sortiert!

17 Quicksort: Vorgehensweise
Wähle das erste Element der Liste als Drehpunkt-Element („Pivot“) Bestimme die erste Teilliste mit Elementen <= Pivot Sortiere diese Teilliste rekursiv mit Quicksort Bestimme die zweite Teilliste mit Elementen > Pivot Füge die sortierten Teillisten zu einer Liste zusammen

18 Quicksort at Work Sortiere (list 11 8 7 14):
Wähle das erste Element von '( ) als Drehpunkt-Element:11 Bestimme die erste Teilliste mit Elementen <= 11: '(8 7) Sortiere diese Teilliste Wähle das erste Element von '(8 7) als Drehpunkt-Element: 8 Bestimme die erste Teilliste mit Elementen <= 8: '(7) Wähle das erste Element von '(7) als Drehpunkt-Element: 7 Bestimme die Teilliste mit Elementen <= 7: empty Sortiere diese Teilliste  Ergebnis empty Bestimme die zweite Teilliste mit Elementen > 7: empty Füge (empty 7 empty) zu einer Liste zusammen -> (list 7) Bestimme die zweite Teilliste mit Elementen > 8:  empty Füge ((list 7) 8 empty) zu einer Liste zusammen  (list 7 8) Bestimme…

19 Quicksort at Work (list 8 7) (list 7) empty 7 8 (list 7 8) 11
14 (list )

20 Quicksort schematisch
Liste, die sortiert werden soll Sortierprozess für Partition mit Werten kleiner/gleich Drehpunkt Sortierprozess für die Partition mit Elementen größer als Drehpunkt sortierte Liste Drehpunkt (Pivot-Element)

21 Quicksort Algorithmus
quicksort unterscheidet zwei Fälle: Ist die Eingabe leer, wird empty zurückgegeben Ansonsten wird eine Rekursion durchgeführt. Jede Teilliste wird separat mit quicksort sortiert Die beiden sortierten Versionen der zwei Listen werden dann mit append kombiniert ;; quicksort2: (listof number) -> (listof number) (define (quicksort2 alon) (cond [(empty? alon) empty] [else (append (quicksort2 (less-or-equal (rest alon) (first alon))) (list (first alon)) (quicksort2 (greater-than (rest alon) (first alon)))) ]))

22 Hilfsfunktionen von Quicksort
greater-than filtert die Elemente heraus, die größer als threshold sind: less-or-equal filtert die Elemente heraus, die kleiner als oder gleich threshold sind: (define (greater-than alon threshold) (filter1 > alon threshold))) (define (less-or-equal alon threshold) (filter1 <= alon threshold)))

23 Auswertungsbeispiel Quicksort
(quicksort (list )) = (append (quicksort (list 8 7)) (list 11) (quicksort (list 14))) = (append (append (quicksort (list 7)) (list 8) (quicksort empty)) = (append (append (append (quicksort empty) (list 7) = ...

24 Auswertungsbeispiel Quicksort
= (append (append (append empty (list 7) empty) (list 8) (list 11) (quicksort (list 14))) = (append (append (list 7) = (append (list 7 8) = ...

25 mergesort: Die Idee Idee: Teile die Liste in der Mitte
Wende die Funktion rekursiv auf die zwei Teillisten an Mische die sortierten Teillisten zu einer neuen geordneten Liste zusammen

26 Zusammenmischen von zwei geordneten Listen
Gegeben sind zwei geordnete Listen ls-1 und ls-2. Wie kann man sie in einer geordneten Liste zusammenmischen? ls-1 ls-2 1 2 4 7 3 5 6 8 Vergleichen und kopieren des kleineren Elements, dann weitergehen sorted-list 1 2 3 4 5 6 7 8

27 Zusammenmischen von zwei geordneten Listen
(define (merge ls1 ls2) (cond [(empty? ls1) ls2] [(empty? ls2) ls1] [(<= (first ls1) (first ls2)) (cons (first ls1) (merge (rest ls1) ls2)) ] [else (cons (first ls2) (merge ls1 (rest ls2)))] )

28 mergesort: der Algorithmus
(define (mergesort alon) (local ((define (merge-step left right) (cond [(>= left right) alon] [else (local ( (define mid (floor (/ (+ left right) 2))) (define left-list (mergesort (extract alon left mid))) (define right-list (mergesort (extract alon (+ mid 1) right)))) (merge left-list right-list) ) ] ))) (merge-step 1 (length alon))))

29 mergesort: der Algorithmus
(define (extract alon left right) (cond [(empty? alon) empty] [(> left right) empty] [(> left 1) (extract (rest alon) (- left 1) (- right 1))] [else (cons (first alon) alon (+ left 1) right))]))

30 Inhaltsverzeichnis Einführung in generativ rekursive Funktionen
Sortieren mittels generativ rekursiver Prozeduren Richtlinien für den Entwurf von generativ rekursiven Prozeduren Strukturelle versus generative Rekursion Backtracking: Durchlaufen von Graphen

31 Richtlinien für den Entwurf von generativ rekursiven Prozeduren
Verstehe die Natur der Daten der Prozedur Beschreibe den Prozess bzgl. der Daten durch Entwurf einer neuen Struktur oder Partitionieren einer Liste von Zahlen. Unterscheide zwischen den Eingabe-Daten die trivial verarbeitet werden können, und denen, die nicht trivial verarbeitet werden können. Die Generierung von Problemen ist der Schlüssel zum Entwurf von Algorithmen Die Lösungen der generierten Probleme müssen kombiniert werden

32 Zurück zu den 6 Stufen des Designs
Datenanalyse und –entwurf Analysiere und bestimme Datensammlungen, welche das Problem darstellen Vertrag, Absicht, Kopf (Header) Lege fest, was die Funktion tut Erkläre in natürlicher Sprache, wie sie funktioniert Funktionsbeispiele Zeige, wie der Algorithmus für bestimmte Eingaben verfährt Vorlage Folge einer generellen Vorlage Definition Beantworte die Fragen, die die Vorlage vorgibt Testen Teste die fertig gestellten Funktionen Beseitige die Fehler

33 Allgemeine Vorlage für generative Prozeduren
(define (generative-recursive-fun problem) (cond [(trivially-solvable? problem) (determine-solution problem)] [else (combine-solutions ... problem ... (generative-recursive-fun (generate-problem-1 problem)) ... (generate-problem-n problem)))]))

34 Prozedurdefinition Was ist ein trivial lösbares Problem und die dazugehörige Lösung? Wie generieren wir neue Probleme, die leichter zu lösen sind als das ursprüngliche Problem? Gibt es ein neues Problem, das wir generieren, oder gibt es viele? Ist die Lösung für das gegebene Problem die gleiche wie für die (eines der) neuen Probleme? Oder müssen wir die Lösungen kombinieren, um eine Lösung für das ursprüngliche Problem zu erstellen? Und wenn das so ist, benötigen wir dann Teile der Daten des ursprünglichen Problems?

35 Terminierung strukturell rekursiver Prozeduren
Bisher hat jede Funktion immer eine Ausgabe für eine gültige Eingabe produziert  Die Evaluierung der strukturell rekursiven Prozeduren hat immer terminiert. Wesentliches Merkmal unseres Rezepts für strukturell rekursive Prozeduren: Jeder Schritt der natürlichen Rekursion konsumiert eine direkte Komponente der Eingabe und nicht die Eingabe selbst Da die Daten hierarchisch konstruiert sind, ist es sicher, dass die Eingabe in jedem Schritt kleiner wird Früher oder später wird die Prozedur ein atomares Datum konsumieren und terminieren

36 Terminierung generativ rekursiver Prozeduren
Dieses Merkmal gilt nicht für generativ rekursive Funktionen Die interne Rekursion konsumiert nicht eine direkte Komponente der Eingabe, sondern irgendein neues Datum, das aus der Eingabe generiert wird Ein Rekursionsschritt kann potenziell immer wieder die ursprüngliche Eingabe generieren und somit die Evaluierung daran verhindern, jemals ein Ergebnis zu produzieren Wir sagen, dass das Programm in eine endlose Schleife gerät

37 Nicht terminierende Programme
Was passiert, wenn wir die folgenden drei Ausdrücke ans Ende des Definition-Fensters von DrScheme setzen und dann auf execute klicken? Produziert der zweite Ausdruck jemals einen Wert, so dass der dritte Ausdruck evaluiert werden kann, um die Fläche verschwinden zu lassen? (start WIDTH HEIGHT) (move-until-out (make-ball )) (stop) Could this happen with any of the functions designed according to our old recipes?  

38 Nicht terminierende Programme
Kleine Fehler bei der Prozessdefinition können Endlosschleifen hervorrufen: ;; less-or-equal: (list-of-numbers) number -> (list-of-numbers) (define (less-or-equal alon threshold) (cond [(empty? alon) empty] [else (if (<= (first alon) threshold) (cons (first alon) (less-or-equal alon threshold)) (less-or-equal (rest alon) threshold))])) statt (rest alon) Quicksort terminiert nicht mit der neuen Funktion (quick-sort (list 5)) = (append (quicksort (less-or-equal 5 (list 5))) (list 5) (quicksort (greater-than 5 (list 5)))) = (append (quicksort (list 5))

39 Terminierungsargument
Das Terminierungsargument ist ein zusätzlicher Schritt im Designrezept für generativ rekursive Funktionen Das Argument erklärt: warum der Prozess für jede Eingabe eine Ausgabe liefert wie die Funktion diese Idee implementieren kann wann der Prozess eventuell nicht terminieren würde

40 Terminierungsargument für Quicksort
Bei jedem Schritt teilt quicksort die Liste mit less-or-equal und greater-than in zwei Teillisten. Jede dieser Funktionen liefert eine Liste, die kleiner als die Eingabeliste (zweites Argument) ist, sogar dann, wenn das Drehpunktelement (erstes Argument) ein Element der Liste ist. Somit verarbeitet jede rekursive Anwendung von quicksort eine Liste, die auf jeden Fall kürzer ist als die Eingabeliste. Letztendlich bekommt quick-sort eine leere Liste und liefert empty.

41 Neue Terminierungs-Fälle
Das Argument der Terminierung kann eventuell zusätzliche Terminierungs-Fälle aufdecken. Dieses Wissen kann dem Algorithmus hinzugefügt werden: So liefert (less-or-equal N (list N)) und (greater-than N (list N)) immer empty ;; quick-sort : (listof number) -> (listof number) (define (quick-sort alon) (cond [(empty? alon) empty] [(empty? (rest alon)) alon] [else (append (quick-sort (less-or-equal (rest alon) (first alon))) (list (first alon)) (quick-sort (greater-than alon (first alon)))) ]))

42 Richtlinien für den Entwurf von generativen Prozeduren
Phase Ziel Weg Beispiele Den Ein-/Ausgabe-Prozess und den Berechnungsvorgang mit Beispielen beschreiben Entwirf und zeige Beispiele trivial lösbarer Probleme Entwirf und zeige Beispiele, die rekursives Vorgehen erfordern Stelle dar, wie man die Beispiele durchgeht Rumpf (Body) Einen Algorithmus definieren Formuliere Tests für trivial lösbare Probleme Formuliere Antworten für die trivialen Fälle Zeige, wie man aus den gegebenen Problemen neue generiert Zeige, wie man die Lösungen dieser Probleme zu einer Gesamtlösung für das gegebene Problem kombiniert Terminierung Zeigen, dass der Algorithmus für alle möglichen Eingaben terminiert Zeige, dass die Eingaben für die rekursive Anwendung kleiner als die gegebene Eingabe sind

43 Inhaltsverzeichnis Einführung in generativ rekursive Funktionen
Sortieren mittels generativ rekursiver Prozeduren Richtlinien für den Entwurf von generativ rekursiven Prozeduren Strukturelle versus generative Rekursion Backtracking: Durchlaufen von Graphen

44 Strukturelle Rekursion als Spezialfall der generativen Rekursion
Vorlage für generative Rekursion (define (generative-recursive-fun problem) (cond [(trivially-solvable? problem) (determine-solution problem)] [else (combine-solutions problem (generative-recursive-fun (generate-problem problem)))])) trivially-solvable? empty? generate-problem rest Vorlage für Listenverarbeitung (define (generative-recursive-fun problem) (cond [(empty? problem) (determine-solution problem)] [else (combine-solutions problem (generative-recursive-fun (rest problem)))]))

45 Strukturelle vs. generative Rekursion
Gibt es einen Unterschied zwischen struktureller und generativer Rekursion? Strukturell rekursive Funktionen scheinen lediglich Spezialfälle generativer Rekursion zu sein Aber: Diese „alles ist gleich“-Einstellung hilft beim Verständnis des Entwurfsprozesses nicht weiter Strukturell und generativ rekursive Funktionen werden mit jeweils anderen Ansätzen entworfen und haben unterschiedliche Konsequenzen Strukturelle Rekursion Generative Rekursion Beruht auf systematischer Datenanalyse Setzt tiefen Einblick in den Problem-lösungsprozess voraus Führt zu naturgemäß terminierenden Funktionen Benötigt ein Terminierungs-Argument

46 Größter gemeinsamer Teiler (GCD)
(GCD = engl. greatest common denominator) Beispiele: 6 und 25 sind beides Zahlen mit mehreren Teilern: 6 ist ganzzahlig teilbar durch 1, 2, 3, und 6; 25 ist ganzzahlig teilbar durch 1, 5, und 25. Der größte gemeinsame Teiler von 25 und 6 ist 1. 18 und 24 haben viele gemeinsame Teiler: 18 ist ganzzahlig teilbar durch 1, 2, 3, 6, 9, 18; 24 ist ganzzahlig teilbar durch 1, 2, 3, 4, 6, 8, 12, 24. Der größte gemeinsame Teiler ist 6.

47 GCD auf Basis struktureller Rekursion
Testet für jede Zahl, i = [min(n,m), …,1], ob sie n und m ganzzahlig teilt und liefert die erste solche Zahl. ;; gcd-structural : N[>= 1] N[>= 1] -> N ;; structural recursion using data definition of N[>= 1] (define (gcd-structural n m) (local ((define (first-divisor i) (cond [(= i 1) 1] [(and (= (remainder n i) 0) (= (remainder m i) 0)) i] [else (first-divisor (- i 1))] ) (first-divisor (min m n)))) Ineffizient bei großen Zahlen!

48 Analyse der strukturellen Rekursion
gcd-structural testet blind jede natürliche Zahl kleiner als min(n,m), ob sie sowohl n als auch m ganzzahlig teilt und gibt die erste solche Zahl zurück Diese Strategie funktioniert nur für kleine Zahlen gut Für die folgende Berechnung muss gcd-structural – 177 = Zahlen testen! (gcd-structural )  177 Selbst schnelle Rechner brauchen Minuten dafür Fügen Sie die Definition von gcd-structural in das Definition-Fenster ein und evaluieren Sie im Interaktions-Fenster folgenden Ausdruck … und gehen Sie dann eine Weile Kaffee trinken  (time (gcd-structural )) Dual Xeon, 2x 3 GHz: >30 min

49 Der Euklidische Algorithmus
Der Euklidische Algorithmus zur Bestimmung der größten gemeinsamen Teiler (GCD) zweier ganzer Zahlen ist einer der ältesten bekannten Algorithmen, Erscheint in Euklids Elementen ca. 300 v. Chr. Der Algorithmus wurde aber wahrscheinlich nicht von Euklid entdeckt; es könnte sein, dass der Algorithmus bereits 200 Jahre früher bekannt war. Erkenntnis: Für zwei natürliche Zahlen n und m, n>m, GCD(n, m) = GCD(m,rest(n/m)). (gcd larger smaller) = (gcd smaller (remainder larger smaller)) Beispiel: GCD(24, 18) = GCD(18, remainder(24/18)) = GCD(18, 6) = GCD(6,0) = 6

50 GCD: generativer Algorithmus
;; gcd-generative : N[>= 1] N[>=1] -> N (define (gcd-generative n m) (local ((define (clever-gcd larger smaller) (cond [(= smaller 0) larger] [else (clever-gcd smaller (remainder larger smaller))])) ) (clever-gcd (max m n) (min m n)))) clever-gcd basiert auf generativer Rekursion: Der trivial lösbare Fall ist smaller = 0. Der generative Schritt ruft clever-gcd mit smaller und (remainder larger smaller) auf (gcd-generative ) nur 9 Iterationen!

51 Der Euklidische Algorithmus
Sei n = qm + r, dann teilt jede Zahl u, welche n und m teilt (n = su, m = tu), auch r r = n – qm = su – qtu = (s – qt)u Jede Zahl v, die m und r teilt (m = s'v, r = t'v ), teilt auch n n = qm + r = qs'v + t'v = (qs' + t')v Jeder gemeinsame Teiler von n und m ist auch ein gemeinsamer Teiler von m und r. gcd(n,m) = gcd(m,r) Es reicht aus, den Prozess mit m und r weiterzuführen Da der absolute Wert von r kleiner ist als m, werden wir r = 0 nach endlich vielen Schritten erreichen

52 Was sollte man benutzen?
Frage: Soll man daraus schließen, dass generative Rekursion besser ist als strukturelle Rekursion? Antwort: Nein, nicht automatisch. Selbst eine gut entworfene generative Prozedur ist nicht immer schneller als die strukturelle Rekursion So gewinnt quicksort gegenüber insertion sort nur für große Listen Strukturelle Rekursion ist einfacher zu entwerfen Generativ rekursive Prozeduren zu entwerfen erfordert oft fundierte mathematische Kenntnisse Strukturelle Rekursion ist einfacher zu verstehen Es könnte schwierig sein, die Idee des generativen Schritts zu verinnerlichen.

53 Was sollte man benutzen?
Beginne mit struktureller Rekursion. Wenn sie zu langsam ist, versuche generative Rekursion einzusetzen. Dokumentiere die Problemgeneration mit guten Beispielen, finde ein gutes Terminierungs-Argument.

54 Inhaltsverzeichnis Einführung in generativ rekursive Funktionen
Sortieren mittels generativ rekursiver Prozeduren Richtlinien für den Entwurf von generativ rekursiven Prozeduren Strukturelle versus generative Rekursion Backtracking: Durchlaufen von Graphen

55 Graphen traversieren Scheme – Listen Darstellung A B E C F D G
(define Graph '((A (B E)) (B (E F)) (C (D)) (D ()) (E (C)) (F (D G)) (G ()))) Scheme – Listen Darstellung Ein Graph ist eine Sammlung von Knoten und Kanten. Die Kanten repräsentieren gerichtete Verbindungen zwischen den Knoten. Kann benutzt werden, um folgendes zu beschreiben: Einen Plan von Einbahnstraßen in einer Stadt, Beziehungen zwischen Personen, Verbindungen im Internet, etc.

56 Graphen traversieren ;; false, if there is no path A B E C F D G
;; find-route : node node graph -> (listof node) ;; to create a path from origination to destination in G ;; false, if there is no path (define (find-route origination destination G) ...) A B E C F D G (find-route 'C 'D Graph) = (list 'C 'D) (find-route 'E 'D Graph) = (list 'E 'C 'D) (find-route 'C 'G Graph) = false

57 Backtracking-Algorithmen
Ein Backtracking-Algorithmus folgt einer bestimmten Vorgehensweise: Verfolge einen (möglichen) Lösungsweg, bis die Lösung gefunden wurde (Erfolg!  terminiere) oder der Weg nicht fortgesetzt werden kann. Wenn der Weg nicht fortgesetzt werden kann: gehe den Weg zurück bis zur letzten Verzweigungsmöglichkeit, wo es noch nicht gewählte Alternativen gibt, wähle dort eine noch nicht gewählte Alternative und mache weiter bei Schritt 1. Wenn der Ausgangspunkt erreicht ist und es keine Alternativen mehr gibt: Misserfolg!  terminiere Anwendungen: N-Damen-Problem, Wege in Graphen finden

58 Graphen traversieren - Beispiel
Finde den Weg von Knoten A nach G! BACKTRACK ! A B E C F D G

59 Graphen traversieren Ist der Startknoten gleich dem Zielknoten, ist das Problem trivial; die Lösung lautet (list destination). Ansonsten: versuche einen Weg von allen Nachbarknoten des Startknotens aus zu finden (define (find-route origination destination aGraph) (cond [(symbol=? origination destination) (list destination)] [else ... (find-route/list (neighbors origination aGraph) destination aGraph) ...]))

60 Nachbarknoten neighbors ist der Funktion contains-doll? ähnlich
;; neighbors : node graph -> (listof node) ;; to lookup the node in graph (define (neighbors node graph) (cond [(empty? graph) (error 'neighbors "can't happen")] [(symbol=? (first (first graph)) node) (second (first graph))] [else (neighbors node (rest graph))]))

61 Graphen traversieren find-route/list
Verarbeitet eine Liste von Knoten Ermittelt für jeden von ihnen, ob ein Weg zum Zielknoten in diesem Graphen existiert ;; find-route/list : ;; (listof node) node graph -> (listof node) or false (define (find-route/list lo-orig dest aGraph) ...) Das Ergebnis von find-route hängt vom find-route/list Ergebnis ab, das eins der folgenden sein kann: Ein Weg von einem der Nachbarknoten zum Zielknoten false, wenn kein Weg von einem der Nachbarn aus gefunden werden konnte

62 Graphen traversieren (define (find-route origination destination aGraph) (cond [(symbol=? origination destination) (list destination)] [else (local ((define possible-route (find-route/list (neighbors origination aGraph) destination aGraph))) [(boolean? possible-route) ...] [else (cons? possible-route) ...]))]))

63 Graphen traversieren (define (find-route origination destination aGraph) (cond [(symbol=? origination destination) (list destination)] [else (local ((define possible-route (find-route/list (neighbors origination aGraph) destination aGraph))) [(boolean? possible-route) false] [else (cons origination possible-route)]))]))

64 Graphen traversieren - Beispiel
Finde den Weg von Knoten A nach G! A B E C F D G Nachbarn von A

65 Graphen traversieren (define (find-route/list lo-Os dest aG) (cond
B E C F D G (define (find-route/list lo-Os dest aG) (cond [(empty? lo-Os) false] [else (local ((define possible-route (find-route (first lo-Os) dest aG))) [(boolean? possible-route) (find-route/list (rest lo-Os) dest aG)] [else possible-route]) ) ]) A B E C F D G

66 Graphen traversieren B C D A B, E, C ist ein Zyklus E F G
Die Funktion terminiert nicht in einem Graph mit einem Zyklus: (find-route 'B 'D Cyclic-graph) = ... (find-route/list (list 'E 'F) 'D Cyclic-graph) ... = ... (find-route 'E 'D Cyclic-graph) ... = ... (find-route/list (list 'C 'F) 'D Cyclic-graph) ... = ... (find-route 'C 'D Cyclic-graph) ... = ... (find-route/list (list 'B 'D) 'D Cyclic-graph) ... = ... (find-route 'B 'D Cyclic-graph) ... = ...

67 Zusammenfassung Es gibt Probleme, die mit der strukturellen Rekursion nicht oder nicht optimal gelöst werden Generative Rekursion basiert auf dem Prinzip „Teile und Herrsche“ Das Design Rezept muss für generativ rekursive Funktionen angepasst werden Insbesondere muss ein Terminierungsargument mitgeliefert werden Strukturell rekursive Funktionen sind eine Teilmenge der generativ rekursiven Funktionen Falls beide Strategien möglich sind, muss die Auswahl fallbasiert getroffen werden Man kann nicht sagen, eine Klasse ist besser als die andere


Herunterladen ppt "Grundlagen der Informatik 1 Thema 6: Generative Rekursion"

Ähnliche Präsentationen


Google-Anzeigen