Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

2. 5 Verzweigte Rekursion und Backtracking-Verfahren 2. 5

Ähnliche Präsentationen


Präsentation zum Thema: "2. 5 Verzweigte Rekursion und Backtracking-Verfahren 2. 5"—  Präsentation transkript:

1 2. 5 Verzweigte Rekursion und Backtracking-Verfahren 2. 5
2.5    Verzweigte Rekursion und Backtracking-Verfahren 2.5.1    Türme von Hanoi

2 Türme von Hanoi: Logisches Puzzle
Gegeben: 3 Pflöcke (start, ziel, hilf), verschieden große Scheiben, der Größe nach geordnet, alle auf Pflock start. Ziel: alle Scheiben auf Pflock ziel bewegen, dort wieder der Größe nach geordnet. Bedingung: Auch zwischendurch darf niemals eine größere Scheibe auf einer kleineren liegen!

3 Lösungsstrategie: rekursiv!
Sei n die Höhe des jeweils zu versetzenden Turmes. move(n,start,ziel,hilf) Falls n=1, einzige Scheibe von start nach ziel sonst 1. move(n-1,start,hilf,ziel); 2. letzte Scheibe von start nach ziel; 3. move(n-1,hilf,ziel,start).

4 Implementation in Java
import java.io.*; public class hanoi { public static void move(String start, String goal) { System.out.println("Move from "+start+" to "+goal); } public static void moves(int n, String start, String goal, String aux) { if (n==1) move(start,goal); else { moves(n-1,start,aux,goal); move(start,goal); moves(n-1,aux,goal,start); } } public static void main(String args[]) { moves(3,"a","c","b");

5 2.5.2    Erzeugung fraktaler Muster durch verzweigt rekursive Turtle-Programme
Beispiel: Koch-Kurve (Schneeflockenkurve)

6 Turtle: virtueller Zeichenroboter
Zustand: Position Blickrichtung Grundbefehle: moveTurtle (Vorwärts- bzw. Rückwärtsschritt) turnTurtle (Drehung auf der Stelle) Turtle kann eine Spur hinterlassen und so Figuren zeichnen.

7 Koch-Kurve in Java: private static void kochstep(TgFrame f, int n, float step) { if (n==0) { f.moveTurtle(step); } else { kochstep(f,n-1,step/3); f.turnTurtle(60); kochstep(f,n-1,step/3); f.turnTurtle(-120); }

8 Beispiel: Binär verzweigter Baum
Ein Baum der Verzweigungstiefe n besteht aus einem Stamm, an dem jeweils im 45 Grad-Winkel nach rechts und links ein Teilbaum gleicher Struktur mit Verzweigungstiefe n-1 angesetzt wird.

9 Implementation in Java:
private static void tree(TgFrame f, int depth, double step) { if (depth==1) { f.moveTurtle(step); f.moveTurtle(-step); } else { f.moveTurtle(step); f.turnTurtle(-45); tree(f,depth-1,step/1.75); f.turnTurtle(90); f.moveTurtle(-step); }

10 Backtracking: Hofstadters MU-Puzzle
Backtracking-Verfahren als Probieren nach dem Tiefensuche-Prinzip Beispiel: MU-Puzzle von Hofstadter Gegeben ist folgendes Zeichensystem: Alphabet:     M, I, U Startwort:    "MI" Zielwort:      "MU" Operatoren (x,y stehen als Variablen für Teilstrings): (R1) xI  xIU (R2) Mx  Mxx (R3) xIIIy  xUy (R4) xUUy  xy Frage: Ist "MU" aus "MI" ableitbar?

11 2.5.3 Backtracking: Hofstadters MU-Puzzle
Backtracking laut Informatik-Duden:   Backtracking bezeichnet ein Trial-and-Error-Verfahren, bei dem man versucht, eine Teillösung eines Problems inkrementell zu einer Gesamtlösung auszubauen. Falls in einer gewissen Situation ein weiterer Ausbau nicht mehr möglich ist (Sackgasse), müssen frühere Teilschritte zurückgenommen werden (daher Backtracking!). Zurücknehmen und erneutes Ausbauen wird solange wiederholt, bis eine Lösung gefunden wird oder bis man erkennt, dass im Rahmen der vorgegebenen Beschränkungen keine Lösung existiert. Im negativen Fall muss der (endliche) "Suchraum" vollständig exploriert werden, was i.d.R. exponentielle Komplexität bedeutet. Beispiel: Suche in einem Irrgarten.

12 Beispiel: MU-Puzzle von Hofstadter
Gegeben ist folgendes Zeichensystem: Alphabet: M, I, U Startwort (z.B.): "MI" Zielwort (z.B.): "MU" Operatoren (x,y sind Variablen für Teilstrings): (R1) xI  xIU (R2) Mx  Mxx (R3) xIIIy  xUy (R4) xUUy  xy Frage: Ist "MU" aus "MI" ableitbar?

13 Allgemein: „Interpolationsproblem“
Gegeben: Zustandsmenge S, eine Menge von Operatoren O, d.h. jeder op aus O ist von der Art op: S  S, Startzustand s und Zielzustand z (beide aus S). Frage: Gibt es eine Operatorsequenz op1, ... opn mit         z = opn(opn-1( .... op1(s) ... )) ?

14 Ausgabe: eine Operatorsequenz (als Liste (java.Vector))
Parameter: s (aktueller Zustand), z (Zielzustand) und t (verbleibende Suchtiefe). Ausgabe: eine Operatorsequenz (als Liste (java.Vector)) muTrack(s, z: S, t: int)  Liste von O { wenn s = z dann rückgabe [ ]; /* leere Liste */ wenn t = 0 rückgabe null; /* "Fehlanzeige" */ res: Liste von O; /* späterer Ausgabeparameter */ n: S; /* nächster Folgezustand */ für alle op in O führe_aus { n := op(s); wenn nicht n = null /* d.h. Operator anwendbar */ dann { res := muTrack(n,z,t-1); wenn nicht res = null dann rückgabe mitErstem(op,res); } } rückgabe null; /* "Fehlanzeige" */ }

15 3 Speicherverfahren 3.1 Strukturierte Daten
Definition (strukturiertes Datum): Ein strukturiertes Datum ist ein Tupel aus             ( Adresse a, Knoteninhalt k, Zeigerfeld z* ) wobei die Adresse ein Wort, der Knoteninhalt wiederum ein ordinaler oder strukturierter Typ und das Zeigerfeld eine Folge von Adressen oder Relationen zwischen Adressen ist, z.B: Adresse Knoteninhalt * *  ... *  Speicherung: injektive Abbildung von einer Datenstruktur bestehend aus Knoten mit Inhalten und Relationen (zu anderen Knoten der Datenstruktur) auf Adressen von Speicherzellen.

16 3.2 Beispiele für strukturierte Daten
Lineare Listen, gekettet oder doppelt gekettet gespeichert. Speicherstruktur einer doppelt geketteten Liste:          Null Kopf : (k1, Null * , *  2) 2: (k2, 1  * , *  3) ... Ende n: (kn , n-1  * , *  Null) Null Durch 1: (k1, n  * , *  2) …… n: (kn , n-1  * , *  1) entsteht eine zirkuläre Liste.

17 Speicherung eines Arrays:
Wir betrachten ein Array a [ 0..n1, 0..n2, ..., 0..nk] . Annahme: die Speicherung beginnt in e, und für jeden Knoten sind r Zellen notwendig. Dann wird das Element    a [ x1, x2, ..., xk ] an folgender Adresse abgelegt: e + r ( x1 (n2+ 1)(n3 + 1) … (nk+ 1) x2 (n3+ 1) ... (nk + 1) + ... xk-1 (nk + 1) xk) Zahlenbeispiel: k = 3, n1 = 2, n2 = 3, n3 = 4, r = 5, e = 23. Dann wird das Element    a [ x1, x2, ..., xk ] an folgender Adresse abgelegt: e + r ( x1 (n2+ 1)(n3 + 1) + x2(n3 + 1) + x3 ), z.B. ist die Adresse des Elements    a [1, 3, 2] : (1 • 4 • • 5 + 2) = 208 

18 Beispiel: Im Fall n1 = n2 = … = nk =9 wird das Element
a [x1, x2 , …, xk] an folgender Adresse abgelegt: e + r •( x1 •10k-1 + x2 • 10k-2 + … + xk-1 • 10 + xk ) = e + r • die Zahl mit der Dezimaldarstellung x1 x2 … xk . Die allgemeine Adressberechnung kann also als Verallgemeinerung der Dezimaldarstellung aufgefasst werden: mit möglicherweise verschiedenen Basen bei den verschiedenen Ziffern xi (Indizes des Elementes).

19 3.3 Verdichtete Speicherung
Eine Menge von Daten kann verdichtet gespeichert werden, wenn fast alle Knoten den gleichen Wert k haben. In diesem Fall verzichtet man auf die Speicherung dieser Knoten und schließt aus ihrem Nichtauftreten, dass sie den Wert k haben. Dann muss jedoch jeder Knoten seine ursprüngliche Position mit sich führen, d.h. ki geht über in (i, ki). Beispiel: Gegeben sei eine lineare Liste (1, k1, *) (2, k2, * ) (3, k3, *) (4, k4, *)(5, k5, * ) (6, k6, *)(7, k7 , *)  ... für deren Elemente gilt: k = k2 = k4 = k5 = k6 = ... Verdichtete Speicherung: (1, k1, *)  (3, k3, *) (7, k7 , *)  ... 

20 3.4 Indizierte Speicherung
Eine lineare Liste K = (k1, ..., kn)  kann in m disjunkte Teillisten aufgespalten werden. Man definiert eine Indexliste, die eine „Liste von Listen“ ist. In der Indexliste werden die Indizes mit einem Namen bzw. der Anzahl der Teillistenelemente und einem Zeiger auf den Beginn der Teillisten abgelegt. Beispiel:   d1     * k2, k2 * k5, k5 * k6, k6 * Null d2     * k1, k1 * Null, .... dm    * k3, k3 * k4, k4 * Null. Anwendung: bei Datenbanken und Betriebssystemen.

21 Beispiel: unter UNIX wird ein mehrfach indiziertes Speicherblocksystem verwendet.
Plätze 0 bis 9: enthalten Adressen von 10 Blocks à 512 Bytes. Platz 10: enthält einen Zeiger auf eine Tabelle von 128 Blockadressen. Platz 11: enthält einen Zeiger auf eine Tabelle von 128 Indexblockadressen, die jeweils eine Tabelle von 128 Blockadressen betreffen (1282 Blockadressen). Platz 12: enthält einen Masterindexblock mit Adressen von 128 Indexblöcken à 128 Blockadressen, womit eine dreifache Indizierung erreicht ist (1283 Blockadressen). Insgesamt können so Blöcke angesprochen werden.

22

23 3.5 Gestreute Speicherung
Seien K = (k1, ..., kn) eine Datenmenge, S ein Speicherbereich mit Anfangsadresse e, w(k) ein Schlüssel zum Knoten k (für jeden Knoten k) (der Schlüssel ist üblicherweise eine Komponente oder eine Kombination von Komponenten des Knoteninhalts; wir nehmen an, dass verschiedene Knoten verschiedene Schlüssel haben) s eine Speicherfunktion vom Schlüsselraum der Daten in den Raum der Adressen. Wir sprechen von gestreuter Speicherung, wenn für jeden Knoten k gilt: Speicheradresse des Knotens k = e + s(w(k)). Das heißt, die Speicheradresse eines Knotens kann aus dem Schlüssel eines Knotens und daher aus dem Knoteninhalt berechnet werden!

24 Anforderungen an die Speicherfunktion s
leicht zu berechnen, sollte nur vom Schlüsselraum, aber nicht von der Datenmenge abhängen, injektiv auf der Menge der vergebenen Schlüssel. Diese Forderungen sind wegen der Größe des Schlüsselraumes nicht gleichzeitig zu erfüllen: der Schlüsselraum, d.h. die Zahl der möglichen Schlüssel ist oft viel größer als die Zahl der zu vergebenden Speicheradressen. Injektivität auf dem ganzen Schlüsselraum ist dann nicht zu erreichen. Oft sind zwar nur relativ wenige Schlüssel tatsächlich vergeben. Welche, hängt aber von der Anwendung ab. Daher verzichtet man oft auf die Injektivität.

25 Kollision, Überlauf: Ist eine Speicherfunktion nicht injektiv, so können derselben Adresse mehrere Schlüssel zugeordnet werden.  Kollision oder Überlauf. Werden derselben Adresse m (m > 1) Schlüsselknoten zugeordnet, sprechen wir von einem Überlauf der Größe m-1.

26 Zur Vermeidung von Kollisionen wird eine weitere Anforderung an die Speicherfunktion gestellt:
Möglichst gleichmäßige Verteilung der Schlüssel auf den Adressraum. Mehr noch: auch Regelmäßigkeiten bei den Schlüsseln sollten nicht auf den Adressraum übertragen werden. Beispiel: Variablennamen beginnen oft mit x, z.B: x1, xi. Hängt die Adresse nur vom ersten Zeichen ab, erhält man dann viele Kollisionen!

27 Beispiele 1. Beispiel für eine Speicherfunktion
Divisionsrestverfahren: s(w(k)) := w(k) mod m wobei m eine Primzahl ist. Warum nicht z.B. m = 2k ? Dann würde die Adresse s(w(k)) nur von den letzten k Bits der Binärdarstellung von w(k) abhängen. Daraus folgt höhere Kollisionsgefahr (bei ungleichmäßiger Verteilung der letzten k Bits in den Schlüsseln)!

28 2. Beispiel für eine Speicherfunktion
Ist w(k) der Schüssel von k, so kann s(w(k)) z.B. durch eine feste Anzahl und eine bestimmte Auswahl von Stellen in w2 gegeben sein: Hier: jeweils letzte und drittletzte Ziffer. w w2 s(w(k)) 723 522729 79 23 529 59 417 173889 89 233 54289 29 3 9 09 999 998001 01


Herunterladen ppt "2. 5 Verzweigte Rekursion und Backtracking-Verfahren 2. 5"

Ähnliche Präsentationen


Google-Anzeigen