Tiefensuche (Depth-First Search) Robert Hilbrich [email protected] 1 Gliederung 1. Einführung 2. Tiefensuche, der Algorithmus 2.1 Modellierung der Arbeitsumgebung 2.2 Programmablauf 3. Korrektheit 3.1 Partielle Korrektheit 3.2 Totale Korrektheit 4. Einordnung der Tiefensuche 2 1. Einführung Adventure: „Indiana Jones and the Fate of Atlantis“ (Fiktive) Spielsituation: • Indy gerät in ein Labyrinth • sucht Ausgang (z.B. Sophia) • Verfügbare Gegenstände: • Peitsche • Revolver • (beliebig lange) rote Schnur Wie geht Indy möglichst systematisch vor? 3 1. Einführung „Indiana Jones And The Fate of Atlantis“ systematische Vorgehensweise: • roten Faden spannen à bereits „gesehene“ Wege markieren • Wenn „Sackgasse“ à zurück zur letzten „ungesehenen“ Gabelung è Vermeidung „doppelter“ Wege 4 1. Einführung Heutige Bedeutung in der Informatik abstrakt: • bekanntes Suchverfahren für Graphenprobleme z.B.: • Ist der Graph verbunden? • Wenn nicht, was sind die Zusammenhangskomponenten? • Existiert ein Kreisschluss im Graphen? konkret: • Routenplaner • Roboter Steuerungslogik • Spiele – Logik • Prolog 5 2. Tiefensuche, der Algorithmus 6 2.1 Modellierung der Arbeitsumgebung Tiefensuche arbeitet mit Graphen • • Sei ein (ungerichteter) Graph G mit V Knoten und E Kanten gegeben alle Knoten seien eindeutig nummeriert (0,1,2,...) Modellierung von G: 1. Knoteninformationen 2. Kanteninformationen 7 2.1 Modellierung der Arbeitsumgebung 1. Feld der Länge V (=maxV) für die Knoten VAR val: array[0..maxV-1]of Integer; • • Index = Knotennummer in G Eintrag = Markierung des Knoten, durch: • i-te Knoten noch nicht „besucht“: val[i] = 0 • j-te Knoten schon „besucht“: val[j] > 0 8 2.1 Modellierung der Arbeitsumgebung 2. Adjazenzliste zur Speicherung der Kanten • • • Array der Länge v von einfach verketteten Listen Index des Array = Knotennummer Listen Elemente = direkte Nachbarn im Graphen Array Listen 0 1 2 1 0 2 2 0 1 3 2 Graph 1 2 3 0 3 9 2.2 Programmablauf Tiefensuche – der Algorithmus PROGRAM tiefensuche; VAR id, k val : INTEGER; : array [1..V-1] of INTEGER; PROCEDURE visit(k : INTEGER); VAR t : Zeiger; BEGIN id := id+1; val[k] := id; t := adj[k]; WHILE t != NULL DO BEGIN IF val[t^.v] = 0 THEN visit(t^.v); t := t^.next; END; END; BEGIN { HAUPTPROGRAMM } id := 0; FOR k := 0 TO (V-1) DO val[k] := 0; FOR k := 0 TO (V-1) DO IF val[k] = 0 THEN visit(k); END; 10 2.2 Programmablauf Tiefensuche – der Algorithmus PROGRAM tiefensuche; VAR id, k : INTEGER; val : array [0..V-1] of INTEGER; PROCEDURE visit(k : INTEGER); VAR t : Zeiger; BEGIN id := id+1; val[k] := id; t := adj[k]; WHILE t != NULL DO BEGIN IF val[t^.v] = 0 THEN visit(t^.v); t := t^.next; END; END; BEGIN id := FOR k FOR k IF END; 0; := 0 TO (V-1) DO val[k] := 0; := 0 TO (V-1) DO val[k] = 0 THEN visit(k); id : „entdeckte“ Knoten k : aktueller Knoten visit: sucht rekursiv alle Knoten einer Zusammenhangskomponente t: Zeiger auf Nachbarknoten von k val[k]: Eintrag im Knotenfeld (Markierung) adj[k]: Adjazenzliste (Nachfolger) 11 2.2 Programmablauf Beispiel: Graph G und zugehörige Adjazenzliste 0 3 2 4 6 7 1 5 0 2 3 1 5 2 0 3 0 4 4 3 6 5 1 8 6 4 7 4 8 5 7 8 12 2.2 Programmablauf Initialisierung Graph G, Adjazenzliste und val[0..maxV-1] Feld FOR k := 0 TO V-1 DO val[k] := 0; Index 0 0 1 3 2 2 3 4 4 6 7 5 6 1 5 7 8 8 Graph 2 5 0 0 3 1 4 4 5 3 4 6 8 Index 0 1 2 3 4 5 7 6 7 8 Eintrag 0 0 0 0 0 0 0 0 0 val[k] Adjazenzliste 13 2.2 Programmablauf Start im Hauptprogramm FOR k := 0 TO V-1 DO IF val[k] = 0 THEN visit(k); 0 3 2 4 6 1 7 5 8 0 1 2 3 4 5 6 7 8 2 5 0 0 3 1 4 4 5 3 4 6 8 7 0 1 2 3 4 5 6 7 8 0 0 0 0 0 0 0 0 0 14 2.2 Programmablauf visit(0) à visit(2) 0 3 2 4 6 1 7 5 8 0 1 2 3 4 5 6 7 8 2 5 0 0 3 1 4 4 5 3 4 6 8 7 0 1 2 3 4 5 6 7 8 1 0 0 0 0 0 0 0 0 15 2.2 Programmablauf visit(2) à check(0) 0 3 2 4 6 1 7 5 8 0 1 2 3 4 5 6 7 8 2 5 0 0 3 1 4 4 5 3 4 6 8 7 0 1 2 3 4 5 6 7 8 1 0 2 0 0 0 0 0 0 16 2.2 Programmablauf visit(0) à visit(3) 0 3 2 4 6 1 7 5 8 0 1 2 3 4 5 6 7 8 2 5 0 0 3 1 4 4 5 3 4 6 8 7 0 1 2 3 4 5 6 7 8 1 0 2 0 0 0 0 0 0 17 2.2 Programmablauf visit(3) à check(0) 0 3 2 4 6 1 7 5 8 0 1 2 3 4 5 6 7 8 2 5 0 0 3 1 4 4 5 3 4 6 8 7 0 1 2 3 4 5 6 7 8 1 0 2 3 0 0 0 0 0 18 2.2 Programmablauf visit(3) à visit(4) 0 3 2 4 6 1 7 5 8 0 1 2 3 4 5 6 7 8 2 5 0 0 3 1 4 4 5 3 4 6 8 7 0 1 2 3 4 5 6 7 8 1 0 2 3 0 0 0 0 0 19 2.2 Programmablauf visit(4) à check(3) 0 3 2 4 6 1 7 5 8 0 1 2 3 4 5 6 7 8 2 5 0 0 3 1 4 4 5 3 4 6 8 7 0 1 2 3 4 5 6 7 8 1 0 2 3 4 0 0 0 0 20 2.2 Programmablauf visit(4) à visit(6) 0 3 2 4 6 1 7 5 8 0 1 2 3 4 5 6 7 8 2 5 0 0 3 1 4 4 5 3 4 6 8 7 0 1 2 3 4 5 6 7 8 1 0 2 3 4 0 0 0 0 21 2.2 Programmablauf visit(6) à check(4) 0 3 2 4 6 1 7 5 8 0 1 2 3 4 5 6 7 8 2 5 0 0 3 1 4 4 5 3 4 6 8 7 0 1 2 3 4 5 6 7 8 1 0 2 3 4 0 5 0 0 22 2.2 Programmablauf visit(4) à visit(7) 0 3 2 4 6 1 7 5 8 0 1 2 3 4 5 6 7 8 2 5 0 0 3 1 4 4 5 3 4 6 8 7 0 1 2 3 4 5 6 7 8 1 0 2 3 4 0 5 0 0 23 2.2 Programmablauf visit(7) à check(4) 0 3 2 4 6 1 7 5 8 0 1 2 3 4 5 6 7 8 2 5 0 0 3 1 4 4 5 3 4 6 8 7 0 1 2 3 4 5 6 7 8 1 0 2 3 4 0 5 6 0 24 2.2 Programmablauf Hauptprogramm à visit(1) 0 3 2 4 6 1 7 5 8 0 1 2 3 4 5 6 7 8 2 5 0 0 3 1 4 4 5 3 4 6 8 7 0 1 2 3 4 5 6 7 8 1 0 2 3 4 0 5 6 0 25 2.2 Programmablauf visit(1) à visit(5) 0 3 2 4 6 1 7 5 8 0 1 2 3 4 5 6 7 8 2 5 0 0 3 1 4 4 5 3 4 6 8 7 0 1 2 3 4 5 6 7 8 1 7 2 3 4 0 5 6 0 26 2.2 Programmablauf visit(5) à check(1) 0 3 2 4 6 1 7 5 8 0 1 2 3 4 5 6 7 8 2 5 0 0 3 1 4 4 5 3 4 6 8 7 0 1 2 3 4 5 6 7 8 1 7 2 3 4 8 5 6 0 27 2.2 Programmablauf visit(5) à visit(8) 0 3 2 4 6 1 7 5 8 0 1 2 3 4 5 6 7 8 2 5 0 0 3 1 4 4 5 3 4 6 8 7 0 1 2 3 4 5 6 7 8 1 7 2 3 4 8 5 6 0 28 2.2 Programmablauf visit(8) à check(5) 0 3 2 4 6 1 7 5 8 0 1 2 3 4 5 6 7 8 2 5 0 0 3 1 4 4 5 3 4 6 8 7 0 1 2 3 4 5 6 7 8 1 7 2 3 4 8 5 6 9 29 2.2 Programmablauf Ende der FOR – Schleife, da k = V FOR k := 0 TO (V-1) DO IF val[k] = 0 THEN visit(k); 0 3 2 4 6 1 7 5 8 0 1 2 3 4 5 6 7 8 2 5 0 0 3 1 4 4 5 3 4 6 8 7 0 1 2 3 4 5 6 7 8 1 7 2 3 4 8 5 6 9 30 2.2 Programmablauf Zusammenfassung • Tiefensuche durchsucht Graphen • Stack (Rekursion) • „Backtracking“ • TS stößt immer zuerst in die maximale Tiefe vor 31 3. Korrektheit 32 3.1 Partielle Korrektheit Partielle Korrektheit mit Hoare Kalkül • sehr umfangreich (2 A4 Seiten) • langwierig • sich wiederholend à monoton à Beschränkung auf Interessantes • Anfangs- und Endbedingungen • Schleifeninvarianten • Rekursion 33 3.1 Partielle Korrektheit Hoare Kalkül und Nassi-Schneidermann Diagramme {P} {I} Anweisung B {Q} { I und B} Anweisung 1 Anweisung 2 {I} { I und nicht (B) } 34 3.1 Partielle Korrektheit Anfangs- und Endbedingungen • V = Anzahl aller Knoten im Graphen • V1 = Anzahl aller markierten Knoten im Graphen • V2 = Anzahl aller nicht markierten Knoten in der aktuellen Zusammenhangskomponente (ZK) • V3 = Anzahl aller nicht markierten Knoten, die nicht in der aktuellen ZK sind • k = aktueller Knoten { P } V=V1+V2+V3 und V>0 Hauptprogramm { Q } V=V1 und V2=0 und V3=0 und k=V 35 3.1 Partielle Korrektheit Anfangs- und Endbedingungen • val[k] = 0 : unbesichtigter Knoten • t^.v : Nachbarknoten von k in Adjazenzliste • (t^.next).v : NachbarNachbarknoten von k in Adjazenzliste P: val[k] = 0 PROCEDURE visit(k) Q: nicht(val[k] = 0) und nicht(val[t^.v] = 0) und nicht(val[(t^.next)^.v] = 0) und nicht ... 36 3.1 Partielle Korrektheit Invarianten im Hauptprogramm (FOR Schleifen durch WHILE ersetzt) FOR k := 0 TO (V-1) DO IF val[k] = 0 THEN visit(k); { I }: V = V1 + V2 + V3 und V-k >= 0 Wiederhole, solange wie k <= V { I } V = V1 + V2 + V3 und { B } k <= V Anweisung(en) { I } V = V1 + V2 + V3 und V - k >= 0 { I } V = V1 + V2 + V3 und V-k >= 0 und nicht (k <= V) 37 3.1 Partielle Korrektheit Invarianten in VISIT WHILE t != NULL DO BEGIN IF val[t^.v] = 0 THEN visit(t^.v); t := t^.next; END; { I } t = NULL oder t^.next = NULL oder (t^.next)^.next = NULL oder ... Wiederhole solange wie nicht (t = NULL) { I und B} (t = NULL oder t^.next = NULL oder (t^.next)^.next = NULL oder ...) und nicht (t = NULL) Anweisung(en) { I } t = NULL oder t^.next = NULL oder (t^.next)^.next = NULL oder ... { I und nicht B} (t = NULL oder t^.next = NULL oder (t^.next)^.next = NULL oder ...) und t = NULL 38 3.1 Partielle Korrektheit Rekursion in VISIT IF val[t^.v] = 0 THEN visit(t^.v); • Nach Hoare gilt ... (Beweis: über Induktion, à K. Schmidt, ThI 1) val[t^.v] = 0 visit(t^.v) nicht(val[t^.v] = 0) und nicht(val[(t^.next)^.v] = 0) und nicht ... 39 3.2 Totale Korrektheit Totale Korrektheit = Terminierung der WHILE Schleifen Hauptprogramm: Für die Terminierungsfunktion f(V,k) gilt: f(V,k) = V – k + 1 , denn: 0 = f(V,k) à k = V + 1 à k > V 40 3.2 Totale Korrektheit Totale Korrektheit = Terminierung der WHILE Schleifen Prozedur VISIT Zusätzliche Funktionen nötig, da Terminierungsfunktion f(t) nach N abbildet. LE sei ein Listenelement nachfolger(LE) := Anzahl der Nachfolger + 1 f(t) = nachfolger(t) f(t) = 0 , falls t != NULL , falls t = NULL 41 4. Einordnung der Tiefensuche 42 4. Einordnung der Tiefensuche Tiefensuche = ein typischer Vertreter von Suchalgorithmen weitere Vertreter : • Breitensuche • Allgemeine Kostensuche • Tiefenbeschränkte Suche • bidirektionale Suche • „Gierige“ Suche • „Hill – Climbing“ Suche • [...] Informationen: http://wwwbrauer.in.tum.de/seminare/web/WS0001/vortrag01.html 43 4. Einordnung der Tiefensuche FAZIT • moderater Speicherverbrauch • Keine Tiefensuche bei großen (infiniten) Tiefen • „Tiefenbeschränkte Suche“ als Alternative 44 Quellen • http://www.informatik.uni-stuttgart.de/ifi/bs/lehre/ei1/1999/htm/graph3.htm • Gerald Futschek; Programmentwicklung und Verifikation; Springer-Verlag Wien New-York; 1989; • Robert Sedgewick; Algorithms in Java; Addison Wesley; 2002 • http://www.informatik.uni-bremen.de/~visser/lectures/ki-1_WS9900/slides/kap_3_suche.pdf • http://wwwbrauer.in.tum.de/seminare/web/WS0001/vortrag01.html • http://www.informatik.uni-stuttgart.de/ifi/bs/lehre/ei1/1999/htm/graph3.htm 45 Abschluss „Indiana Jones – Der Entdecker der Tiefensuche?“ • Indys Vorgehen entspricht dem Ablauf des Algorithmus der Tiefensuche • Dennoch, Indiana Jones ist nicht der Entdecker ... „Die Tiefensuche ist schon aus dem Altertum und aus der griechischen Sage bekannt unter dem Namen Labyrinth-Suche. Dieser bezieht sich auf die Erzählung von dem griechischen Helden Theseus, der nach Kreta reiste, um dort ein Untier namens Minotauros zu erlegen, das dort in einem unterirdischen Höhlensystem, genannt Labyrinth, sein Unwesen trieb. [...] jedenfalls fand Theseus den Minotauros mittels Tiefensuche, und als Markierungshilfsmittel verwendete er das Wollknäuel aus dem Strickzeug der Königstochter Ariadne, [...]“ 46