Notizen Algorithmen und Datenstrukturen (für ET/IT) Wintersemester 2012/13 Dr. Tobias Lasser Computer Aided Medical Procedures Technische Universität München Programm heute Notizen 7 Fortgeschrittene Datenstrukturen 8 Such-Algorithmen 9 Graph-Algorithmen Tiefensuche Breitensuche Kürzeste Pfade 3 Bild-Repräsentation mit Pixeln Notizen • Bild als zwei-dimensionales Feld • einzelnes Element: Pixel (picture element) • hier: jedes Pixel hat einen Grauwert zugewiesen • z.B. von 0 bis 255 4 Segmentierung von Bildern Notizen • Segmentierung: Zusammenfassung von inhaltlich zusammenhängenden Regionen gemäß eines Kriteriums • Beispiel: Segmentierung von Knochen oder Organen 5 Binäre Bilder Notizen • Binäres Bild: jedes Pixel hat entweder Wert 0 oder 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 1 1 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 • Segmentierungs-Problem: identifiziere die beiden “Sterne” → Algorithmus? 6 Problem: Segmentierung von “Sternen” 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 1 1 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 Notizen • Gegeben: binäres Bild mit 0,1 Werten • Gesucht: Regionen im Bild mit nur 1 Werten • Ansatz: • fange an bei Pixel mit Wert 1 • wiederhole: inspiziere unbesuchte Nachbar-Pixel auf Wert 1 • beende falls kein weiterer Nachbar mit Wert 1 existiert 7 Nachbarschaft von Pixeln Notizen N N P N N 4-Nachbarschaft N N N N P N N N N 8-Nachbarschaft • 4-Nachbarschaft von Pixel P: alle direkten Nachbar-Pixel, die Kante mit P gemeinsam haben • auch genannt: Von-Neumann-Nachbarschaft • 8-Nachbarschaft von Pixel P: alle direkten Nachbar-Pixel, die Kante oder Ecke mit P gemeinsam haben • auch genannt: Moore-Nachbarschaft 8 “Stern” als Graph Notizen 1 5 2 3 4 6 7 8 10 11 12 9 13 Segmentierungs-Ansatz von linkem “Stern”: 1 starte mit Pixel 1 2 besuche Nachbarn (Schema: oben, unten, links, rechts) 9 Segmentierungs-Durchlauf als Baum Notizen 1 1 5 3 2 3 4 6 7 8 10 11 12 7 11 9 13 10 12 6 8 13 2 5 4 9 Ansatz auch genannt: Tiefensuche bzw. Depth-First Search (DFS) 10 Tiefensuche (DFS) Notizen Sei G = (V , E ) Graph (gerichtet oder ungerichtet). • Graph repräsentiert mit Adjazenzliste adj • jeder Knoten hat Farb-Markierung farbe: • weiss = noch nicht besucht • grau = besucht, gerade in Bearbeitung • schwarz = besucht, fertig bearbeitet • jeder Knoten hat Vorgänger in Besuch-Reihenfolge pred • (optional) zusätzliche Knoten-Markierungen d und f • Zeitmarke, wann Knoten entdeckt: d (discovered) • Zeitmarke, wann Knoten fertig bearbeitet: f (finalized) 11 Algorithmus DFS Notizen Input: Graph G = (V , E ) Output: Vorgänger-Liste pred, Markierungen d, f DFS(G ): for each (Knoten v ∈ V ) { farbe[v ] = weiss; pred[v ] = NULL; } zeit = 0; for each (Knoten v ∈ V ) { if (farbe[v ] == weiss) DFSvisit(v ); } DFSvisit(v ): farbe[v ] = grau; // v war weiss zeit = zeit + 1; d[v ] = zeit; for each (Knoten u ∈ adj[v ]) { if (farbe[u] == weiss) { pred[u] = v; DFSvisit(u); } } farbe[v ] = schwarz; // v ist fertig zeit = zeit + 1; f[v ] = zeit; 12 DFS: Beispiel-Ablauf 1 Notizen u v w x y z • Knoten u, v, w, x, y, z • farbe direkt im Knoten markiert • d/f im Knoten notiert • pred markiert durch rote Kanten 13 DFS: Beispiel-Ablauf 2 u v w Notizen u v 1/_ 1/_ w u v 2/_ 1/_ w 2/_ 3/_ x y u v 1/_ z x w u y v 2/_ 1/_ z x w u y v 2/_ 1/_ 4/_ 3/_ u v 1/_ z x w u 3/_ 4/5 y v 2/_ 1/_ B z x w u 3/6 z 1/_ 3/6 B 4/5 y z w 2/7 F x z v 2/7 4/5 y 3/_ y B 4/5 x B 4/_ y w 2/_ B x z 3/6 x y z 14 DFS: Beispiel-Ablauf 3 u v 1/_ F F 3/6 z v 1/8 B 4/5 z v 1/8 F 4/5 z 10/ 11 B 9/_ C B 4/5 x w 2/7 3/6 y z 10/ _ B w 9/ 12 2/7 C B 4/5 x z v 1/8 F 3/6 y y u 9/_ C B 3/6 x w 2/7 F 10/ _ z v 1/8 C B 3/6 y u 9/_ 9/_ B x w w 2/7 4/5 z 2/7 4/5 y u 3/6 v F 3/6 x F 1/8 C v 1/8 y u 9/_ u B x w 2/7 w 2/7 4/5 y u v 1/8 B 4/5 F u 2/7 x x w Notizen 3/6 y z 10/ 11 B 15 Spannwald mit DFS Notizen Sei G = (V , E ) Graph. • Spannbaum: Teilgraph G 0 = (V , E 0 ), der ein Baum ist und alle Knoten von G enthält. • existiert nur für zusammenhängende Graphen • Spannwald: Teilgraph G 0 = (V , E 0 ), dessen Zusammenhangskomponenten jeweils Spannbäume der Zusammenhangskomponenten von G sind • DFS erzeugt Spannwald • Knoten sind V , Kanten E 0 ergeben sich aus pred • im Beispiel-Ablauf: Knoten V sind schwarz, Kanten E 0 sind rot • DFS erlaubt Markierung spezieller Kanten: • Rückkanten im Spannwald (markiert mit B) • Vorwärtskanten im Spannwald (markiert mit F) • Cross-Kanten im Spannnwald (markiert mit C) 16 Komplexität von DFS Notizen • DFS erste Schleife wird |V | mal durchlaufen: Θ(|V |) • DFSvisit wird für jeden Knoten genau einmal aufgerufen (zweite Schleife in DFS, rekursiver Aufruf in DFSvisit) • innere Schleife in DFSvisit wird |adj(v )| mal aufgerufen X |adj(v )| = Θ(|E |) v ∈V • Gesamtlaufzeit: Θ(|V | + |E |) • Implementierung gleicher Komplexität auch nicht-rekursiv möglich (mit Stack) 17 Anwendungen von DFS Notizen • Test auf Zusammenhang von Graphen • rufe DFSvisit nur für einen Knoten auf (statt für alle) • falls nicht alle Knoten dadurch besucht: nicht zusammenhängend! • Test auf Zyklenfreiheit in Graphen • Zyklus entdeckt falls Rückkante B gefunden • Rückkante B: in DFSvisit Schleife ist Zielknoten bereits grau 18 Programm heute Notizen 7 Fortgeschrittene Datenstrukturen 8 Such-Algorithmen 9 Graph-Algorithmen Tiefensuche Breitensuche Kürzeste Pfade 19 Breitensuche im “Stern”-Beispiel Notizen • Segmentierung von “Sternen” 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 1 1 1 0 0 0 1 0 0 1 1 1 1 1 0 1 1 1 0 0 1 1 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 • neuer Ansatz: • anstatt direkt nächsten Nachbar ansteuern, erst alle aktuellen Nachbarn abarbeiten → Breitensuche 20 Breitensuche im “Stern”-Beispiel: Ablauf Notizen 1 5 2 3 4 6 7 8 10 11 12 9 13 1 starte mit Pixel 1 2 besuche alle Nachbarn von aktuellem Pixel (Schema: oben, unten, links, rechts) 3 arbeite die Nachbarn der Nachbarn in selber Reihenfolge ab 21 Breitensuche im “Stern”-Beispiel: als Baum Notizen 1 1 5 3 2 3 4 6 7 8 10 11 12 7 9 11 13 10 12 2 6 8 5 9 4 13 Ansatz: Breitensuche bzw. Breadth-First Search (BFS) 22 Breitensuche (BFS) Notizen Sei G = (V , E ) zusammenhängender Graph (gerichtet oder ungerichtet). • Startknoten s ∈ V • Graph repräsentiert mit Adjazenzliste adj • jeder Knoten hat Farb-Markierung farbe: • weiss = noch nicht besucht • grau = besucht, gerade in Bearbeitung • schwarz = besucht, fertig bearbeitet • jeder Knoten hat Vorgänger in Besuch-Reihenfolge pred • (optional) zusätzliche Knoten-Markierung d • Distanz (in Anzahl von Kanten) von Startknoten s • Hilfsmittel: Queue Q 23 Algorithmus BFS Notizen Input: Graph G = (V , E ), Startknoten s ∈ V Output: Vorgänger-Liste pred, Markierung d BFS(G , s): for each (Knoten v ∈ V ) { // Initialisierung farbe[v ] = weiss; pred[v ] = NULL; d[v ] = ∞; } farbe[s] = grau; d[s] = 0; Q = initialize(); Q.enqueue(s); while ( !Q.isEmpty() ) { u = Q.dequeue(); for each (v ∈ adj[u]) { // besuche alle Nachbarn if (farbe[v ] == weiss) { farbe[v ] = grau; d[v ] = d[u] + 1; pred[v ] = u; Q.enqueue(v ); } } farbe[u] = schwarz; // u ist erledigt } 24 BFS: Beispiel-Ablauf 1 Notizen r s t u v w x y • Knoten r, s, t, u, v, w, x, y • farbe direkt im Knoten markiert • d im Knoten notiert • pred markiert durch rote Kanten • Startknoten s 25 BFS: Beispiel-Ablauf 2 r s ∞ t u 0 r ∞ ∞ ∞ w r s 1 ∞ x 1 ∞ ∞ u ∞ v 1 w r s 1 2 x r 2 2 r x v ∞ 2 1 v Q: v u y 3 2 w v u 2 u y x y t 0 t ∞ x s 1 Q: ∞ 2 w 3 2 x 1 v r u 2 x u 2 1 w t ∞ Q: v r w y t 0 Q: ∞ x s 1 y t 0 ∞ ∞ w ∞ Q: u ∞ 1 v 2 t 0 s y t 0 s ∞ Q: v Notizen 3 x y 26 BFS: Beispiel-Ablauf 3 r s 1 t 0 u 2 r 3 2 1 w r s 1 2 x 2 2 1 w r 3 2 x s 1 t 0 u y 3 y u 2 3 y Q: 3 y u 2 Q: 3 2 x t 0 y v u 2 1 w u 3 Q: v v y t 0 s 1 Q: v Notizen 2 v 1 w 2 x 3 y 27 BFS Eigenschaften Notizen Sei G = (V , E ) Graph. • BFS erzeugt Spannbaum G 0 = (V , E 0 ) • Knoten sind V , Kanten E 0 ergeben sich aus pred • im Beispiel-Ablauf: Knoten V sind schwarz, Kanten E 0 sind rot • ausgehend von Startknoten s wird nur Zusammenhangskomponente von Graph durchsucht! • falls G nicht zusammenhängend, wird kein Spannwald berechnet! • falls Stack statt Queue in BFS: DFS-artiger Algorithmus • Unterschied: nur in Zusammenhangskomponente 28 Komplexität von BFS Notizen • BFS erste Schleife (for) wird |V | mal durchlaufen: Θ(|V |) • zweite geschachtelte Schleife (while und for) wird im schlechtesten Fall je einmal für jeden Knoten in Adjazenzliste aufgerufen X |adj(v )| = Θ(|E |) v ∈V • Gesamtlaufzeit: Θ(|V | + |E |) 29 Anwendungen von BFS Notizen • Besuche alle Knoten in Zusammenhangskomponente von Graph • ausgehend von Startknoten s • Berechne Länge der kürzesten Pfade (d.h. Anzahl von Kanten) • ausgehend von Startknoten s • kürzeste Pfade selbst erfordern Modifikation des Algorithmus 30 Programm heute Notizen 7 Fortgeschrittene Datenstrukturen 8 Such-Algorithmen 9 Graph-Algorithmen Tiefensuche Breitensuche Kürzeste Pfade 31 Definition Kürzester Pfad Notizen Sei G = (V , E ) Graph mit Gewichtsfunktion w : E → R. • das Gewicht eines Pfades p = (v1 , . . . , vk ), vi ∈ V , i = 1, . . . , k, ist die Summe der Gewichte aller seiner Kanten w (p) = k−1 X w (vi , vi+1 ) i=1 • das Gewicht des kürzesten Pfades zwischen zwei Knoten u und v (u, v ∈ V ) ist ( min{w (p) : p Pfad von u nach v } Pfad existiert wmin (u, v ) = ∞ sonst 32 Beispiel: kürzeste Pfade Notizen Navigationssystem: HH • Knoten sind Großstädte H • Gewichte der Kanten sind (grobe) 200 350 450 DD 200 300 F 200 Fragestellungen: anderen Städten? 200 K Entfernungen in Kilometern • alle kürzesten Verbindungen von M zu B 250 300 • Kanten sind Autobahnverbindung • kürzester Weg von M nach HH? 200 300 150 KA N 50 S 250 150 M • alle kürzesten Verbindungen zwischen allen Städten? 33 Kürzeste Pfade: Algorithmen Notizen Sei G = (V , E ) gewichteter, gerichteter Graph. • Kürzeste Pfade in Graphen von fixem Startknoten s: • Dijkstra-Algorithmus (nur positive Kantengewichte) Greedy-Algorithmus Komplexität: O(|E | log |V |) • A*-Algorithmus (nur positive Kantengewichte) Komplexität: mit guter Heuristik, besser als Dijkstra • Bellman-Ford-Algorithmus (auch negative Gewichte) Komplexität: O(|V | ∗ |E |) • Kürzeste Pfade in Graphen zwischen allen Knotenpaaren: • Floyd-Warshall-Algorithmus (all pairs shortest path) basiert auf dynamischer Programmierung Komplexität: Θ(|V |3 ) 34 Gegenbeispiel Greedy-Algorithmus Notizen 2 1 1 10 4 4 5 9 3 • Greedy-Schema: füge kürzeste Kante zu Pfad (so dass kein Zyklus entsteht) • • • • Starte bei Knoten 1 füge Kante (1,2) hinzu (Kosten 1) füge Kante (2,3) hinzu (Kosten 4) füge Kante (3,4) hinzu (Kosten 9) • Problem: Pfad von 1 nach 4 hat Kosten 14, kürzester Pfad hat aber Kosten 11! 35 Beispiel: Ablauf Dijkstra-Algorithmus 1 80 1 60 4 60 120 5 Länge 0 100 20 3 Kürzester Pfad 5 2 40 Notizen 120 6 40 Knoten hinzugefügt 1 3 6 Pfad Pfad Länge 5 5 5 60 120 40 36 Beispiel: Ablauf Dijkstra-Algorithmus 2 80 1 2 40 60 100 20 3 5 40 Kürzester Pfad 5 5,6 Länge 0 40 4 60 120 Notizen 120 6 Knoten hinzugefügt 1 3 4 Pfad Pfad Länge 5 5,6 5,6 60 100 160 37 Beispiel: Ablauf Dijkstra-Algorithmus 3 80 1 2 40 60 100 20 3 5 Kürzester Pfad 5 5,6 5,1 Länge 0 40 60 4 60 120 Notizen 120 6 40 Knoten hinzugefügt 2 3 4 Pfad Pfad Länge 5,1 5,6 5,6 140 100 160 38 Beispiel: Ablauf Dijkstra-Algorithmus 4 80 1 2 40 60 100 20 3 60 120 5 4 40 Notizen Kürzester Pfad 5 5,6 5,1 5,6,3 Länge 0 40 60 100 120 6 Knoten hinzugefügt 2 4 Pfad Pfad Länge 5,1 5,6,3 140 120 39 Beispiel: Ablauf Dijkstra-Algorithmus 5 80 1 2 40 60 100 20 3 60 120 5 4 120 6 40 Notizen Kürzester Pfad 5 5,6 5,1 5,6,3 5,6,3,4 Knoten hinzugefügt 2 Länge 0 40 60 100 120 Pfad Pfad Länge 5,1 140 40 Beispiel: Ablauf Dijkstra-Algorithmus 6 80 1 2 40 60 100 20 3 60 120 5 4 40 120 Notizen Kürzester Pfad 5 5,6 5,1 5,6,3 5,6,3,4 5,1,2 Länge 0 40 60 100 120 140 6 fertig 41 Dijkstra-Algorithmus Notizen Sei G = (V , E ) Graph mit Gewicht w : E → R+ (positive reelle Zahlen). • Startknoten s ∈ V für kürzesten Pfad • Graph repräsentiert mit Adjazenzliste adj • jeder Knoten (ausser s) hat Vorgänger im kürzesten Pfad pred • jeder Knoten hat Markierung d • Distanz (bzgl. Gewicht) von Startknoten s • Hilfsmittel: Priority Queue Q 42 Algorithmus: Dijkstra Notizen Input: Graph G = (V , E ), w : E → R+ , Startknoten s ∈ V Output: Vorgänger-Liste pred, Distanz-Markierung d Dijkstra(G , w , s): for each (Knoten v ∈ V ) { // Initialisierung pred[v ] = NULL; d[v ] = ∞; } d[s] = 0; Q = Priority Queue mit Elementen V , Schlüsseln d; while ( !Q.isEmpty() ) { u = Q.extractMin(); for each (v ∈ adj[u] mit v ∈ Q) { if (d[u] + w (u, v ) < d[v ]) { pred[v ] = u; d[v ] = d[u] + w (u, v ); Q.decreaseKey(v , d[v ]); } } } 43 Beispiel: Ablauf Dijkstra-Algorithmus 1 u 1 ∞ v ∞ 2 3 0 5 6 s ∞ ∞ 5 y 1 8 (x, 5) (u, 10) (v, ∞) v u ∞ y (y, ∞) 1 8 10 ∞ 2 x Q: (u, ∞) (v, ∞) (x, ∞) (y, ∞) u 5 6 7 5 2 x v 13 10 s 2 3 0 5 6 s 5 (u, 8) 5 6 7 5 7 2 x 2 3 0 7 5 (y, 7) ∞ 2 3 0 7 5 Q: v 10 s (s,0) 1 10 10 Q: Notizen u 5 y Q: (v, ∞) 7 2 x y (u, 8) (v, 13) 44 Beispiel: Ablauf Dijkstra-Algorithmus 2 u 1 8 v 13 1 8 10 v 9 10 s 2 3 0 5 6 s 5 5 y x Q: (u, 8) (v, 13) u 1 8 5 6 7 5 7 2 x 2 3 0 7 5 Q: Notizen u 2 7 y (v, 9) v 9 10 s 2 3 0 5 6 7 5 5 x 2 7 y Q: 45 Zusammenfassung Notizen 7 Fortgeschrittene Datenstrukturen 8 Such-Algorithmen 9 Graph-Algorithmen Tiefensuche Breitensuche Kürzeste Pfade 46 Notizen