2 KONVEXE HÜLLE 2.6 Korrigierte Algorithmen Algorithmus 2.2’ (Konvexe Hülle Langsam korrigiert) Eingabe: Endliche Menge P von Punkten der Ebene Ausgabe: Liste L der Ecken von conv P in positiver Orientierung 1: E ← ∅ 2: Für alle Paare (p, q) ∈ P × P mit p 6= q mache 3: gültig ← wahr 4: Für alle Punkte r ∈ P \ {p, q} mache → 5: Wenn r liegt streng rechts der gerichteten Strecke − pq oder auf der Gerade pq aber nicht innerhalb von pq dann 6: gültig ← falsch 7: Wenn gültig dann füge (p, q) zu E hinzu 8: Berechne L von E durch Verfolgung. Algorithmus 2.4’ (Konvexe Hülle korrigiert) Eingabe: Endliche Menge P von Punkten der Ebene Ausgabe: Liste L der Ecken von conv P in positiver Orientierung 1: Sortiere die Punkte in P lexikographisch (nach x-Koordinate, bei Gleichheit nach y-Koordinate), was eine Liste p1 , . . . , pn liefert. 2: Packe p1 , p2 in die Liste Lunten (p2 nach p1 ) 3: Für alle i ← 3 bis n mache 4: Füge pi an Lunten an 5: Solange Lunten mehr als 2 Punkte enthält und die letzten 3 Punkte von Lunten keine Linksdrehung ausführen, mache 6: Lösche den vorletzen Punkt aus Lunten . 7: Packe pn , pn−1 in die Liste Loben 8: Für alle i ← n − 2 absteigend bis 1 mache 9: Füge pi an Loben an 10: Solange Loben mehr als 2 Punkte enthält und die letzten 3 Punkte von Loben keine Linksdrehung ausführen, mache 11: Lösche den vorletzen Punkt aus Loben . 12: Lösche den ersten und letzten Punkt von Loben . 13: Füge ganz Loben an Lunten , das Ergebnis ist L. Algorithmische Geometrie, WS 2009/10 8 TUM, M9, Dr. Nico Düvelmeyer 3 SCHNITTPUNKTE VON STRECKEN 3 Schnittpunkte von Strecken → Bemerkung 3.1 (Einschub Balancierter Baum) Zitat von http: // de. wikipedia. org/ wiki/ Suchbaum : Der normale binäre Suchbaum ist zwar einfach zu implementieren, kann jedoch nicht garantieren, dass die oben genannten Operationen insert, delete und find effizient ausgeführt werden können. Dazu gibt es spezielle binäre Suchbäume, welche durch verschiedene Techniken garantieren, dass sie immer balanciert sind, und die Operationen insert, delete und find somit immer effizient (in logarithmischer Zeit) ausgeführt werden können. Einige dieser speziellen Suchbäume sind: • 2-3-4-Baum • AVL-Baum • B-Baum • Rot-Schwarz-Baum • Kd-Baum Auftrag 3.2 Gegeben: n Strecken der Ebene Gesucht: Menge aller Schnittpunkte der abgeschlossenen Strecken, unter Angabe der beteiligten Strecken Die Laufzeit soll ausgabeabhängig möglichst gut sein. Wir vermeiden also so gut wie möglich die Schnittbestimmung von Strecken, die sich nicht schneiden. 3.1 3.1.1 Gleitebenenverfahren Anfangsidee Wir scheiden nur Strecken, deren y-Bereiche sich auch schneiden. Status: Menge der Strecken, die die Gleitgerade schneiden. Ereignispunkte: Anfangs- und Endpunkte der Strecken Algorithmische Geometrie, WS 2009/10 9 TUM, M9, Dr. Nico Düvelmeyer 3 SCHNITTPUNKTE VON STRECKEN 3.1.2 Verfeinerung Status: Sortierte Liste T der Strecken, wie sie die Gleitgerade (von links nach rechts) schneiden. Balancierter Suchbaum. Ereignispunkte: Anfangs-, End- und Schnittpunkte der Strecken, sortiert nach Abarbeitung: Von oben nach unten, bei Gleichheit von links nach rechts. Zu jedem Punkt ist eine Menge dort beginnender Strecken gespeichert. Balancierter Suchbaum. Kein Heap, da bereits enthaltene Punkte nicht nochmals eingefügt werden sollen. Ereigniswarteschlange Q. Algorithmus 3.3 Eingabe: Menge S von Strecken der Ebene Ausgabe: Eine Menge von Schnittpunkten mit Angabe der beteiligten Strecken 1: Funktion SucheSchnittpunkte(S) 2: Initialisiere eine leere Ereigniswarteschlange Q. Füge die Randpunkte der Strecken von S in Q ein. Zu den oberen Randpunkten wird auch die zugehörige Strecke gespeichert. Für horizontale Strecken betrachten wir den linken Randpunkt als oberes Ende (Anfangspunkt). 3: Initialisiere einen leeren Statusbaum T . 4: Solange Q nicht leer ist, mache 5: Bestimme den nächsten Ereignispunkt p aus Q und lösche diesen. 6: BehandelEreignispunkt(p) 7: Unterprogramm BehandelEreignispunkt(p) 8: Bezeichne mit U (p) die Menge der Strecken (aus Q), die in p beginnen. 9: Suche alle Strecken in T , die p enthalten. L(p) sei die Menge jener Strecken, für 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: die p das untere Ende ist, und C(p) die Menge der Strecken von T mit p im Inneren. Wenn L(p) ∪ U (p) ∪ C(p) mehr als eine Strecke enthält, dann Melde p als Schnittpunkt, zusammen mit L(p), U (p) und C(p). Lösche die Strecken in L(p) ∪ C(p) aus T . Füge die Strecken in U (p) ∪ C(p) neu in richtiger Reihenfolge in T ein (horizontale Strecken zum Schluss). ⊲ Löschen und Neueinfügen der Strecken von C(p) vertauscht deren Reihenfolge Wenn U (p) ∪ C(p) = ∅ dann Bestimme linken Nachbarn sl und rechten Nachbarn sr von p in T . SucheNeuesEreignis(sl ,sr ,p) sonst Sei s′ die Strecke am weitesten links von U (p) ∪ C(p) in T , und sl deren linker Nachbar in T . SucheNeuesEreignis(sl ,s′ ,p) falls sl existiert. Sei s′′ die Strecke am weitesten rechts von U (p) ∪ C(p) in T , und sr deren rechter Nachbar in T . SucheNeuesEreignis(s′′ ,sr ,p) falls sr existiert. 24: Unterprogramm SucheNeuesEreignis(sl , sr , p) 25: Wenn sl und sr sich unterhalb der Gleitebene schneiden (inklusive: direkt rechts 26: von p auf dieser), und wenn deren Schnittpunkt noch nicht in Q enthalten ist, dann Füge den Schnittpunkt in Q ein. Algorithmische Geometrie, WS 2009/10 10 TUM, M9, Dr. Nico Düvelmeyer 3 SCHNITTPUNKTE VON STRECKEN Satz 3.4 Alle Schnittpunkte und zugehörige Strecken werden korrekt von Algorithmus 3.3 bestimmt. Beweis Mit Induktion folgt, dass jeder Schnittpunkt auch erkannt wird sobald zwei der beteiligten Strecken benachbart werden. Dies passiert nur an Ereignispunkten. Bei der Behandlung jedes Schnittpunktes p enthält T tatsächlich alle Strecken, die von der Waagerechten geschnitten werden, bis auf Strecken aus U (p). ¤ Satz 3.5 Die Laufzeit von Algorithmus 3.3 für n Strecken mit I Schnittpunkten ist in O((n + I) log n). Beweis Wir betrachten die einzelnen Anweisungen von Algorithmus 3.3. 1: Funktion SucheSchnittpunkte(S) 2: Initialisiere eine leere Ereigniswarteschlange Q. ⊲ Zeit O(1) Füge die Randpunkte der Strecken von S in Q ein. Zu den oberen Randpunkten wird auch die zugehörige Strecke gespeichert. Für horizontale Strecken betrachten wir den linken Randpunkt als oberes Ende (Anfangspunkt). ⊲ 2n mal Grundoperation Suchen. Maximal 2n mal Grundoperation Einfügen in Q oder alternativ Einfügen in Streckenmenge. Dabei Größe von Q maximal 2n. Zeit: O(n log n). 3: Initialisiere einen leeren Statusbaum T . ⊲ Zeit O(1) 4: Solange Q nicht leer ist, mache 5: Bestimme den nächsten Ereignispunkt p aus Q und lösche diesen. ⊲ Maximal 2n + I Durchläufe, Grundoperationen in Q, von Größe maximal 2n + I. Zeit: 2(2n + I) log(2n + I) ∈ O((n + I) log n) 6: BehandelEreignispunkt(p) Beachte: I < n2 , log(2n + I) < log(2n2 ) = log 2 + 2 log n ∈ O(log n). Jeder Durchlauf des innersten Algorithmus SucheNeuesEreignis(sl , sr , p), benötigt Zeit in O(log |Q|), das ist in O(log n). Unterprogramm BehandelEreignispunkt(p) wird maximal 2n + I mal ausgeführt. 7: Unterprogramm BehandelEreignispunkt(p) 8: Bezeichne mit U (p) die Menge der Strecken (aus Q), die in p beginnen. ⊲ Zeit: O(|U (p)|) 9: Suche alle Strecken in T , die p enthalten. L(p) sei die Menge jener Strecken, für die p das untere Ende ist, und C(p) die Menge der Strecken von T mit p im Inneren. ⊲ Zeit: O(log |T | + |L(p) ∪ C(p)|) 10: Wenn L(p) ∪ U (p) ∪ C(p) mehr als eine Strecke enthält, dann 11: Melde p als Schnittpunkt, zusammen mit L(p), U (p) und C(p). ⊲ Zeit O(|L(p) ∪ C(p) ∪ U (p)|) 12: Lösche die Strecken in L(p) ∪ C(p) aus T . ⊲ Zeit: O(|L(p) ∪ C(p)| log |T |) 13: Füge die Strecken in U (p) ∪ C(p) neu in richtiger Reihenfolge in T ein (horizontale Strecken zum Schluss). ⊲ Zeit O(|U (p) ∪ C(p)| log |T |) 14: Wenn U (p) ∪ C(p) = ∅ dann 15: Bestimme linken Nachbarn sl und rechten Nachbarn sr von p in T . ⊲ O(|T |) 16: SucheNeuesEreignis(sl ,sr ,p) ⊲ Zeit: O(log n) 17: sonst 18: Sei s′ die Strecke am weitesten links von U (p) ∪ C(p) in T , ⊲ Zeit: O(log |T |) Algorithmische Geometrie, WS 2009/10 11 TUM, M9, Dr. Nico Düvelmeyer 3 SCHNITTPUNKTE VON STRECKEN und sl deren linker Nachbar in T . ⊲ Zeit: O(log |T |) ′ SucheNeuesEreignis(sl ,s ,p) falls sl existiert. ⊲ Zeit: O(log n) Sei s′′ die Strecke am weitesten rechts von U (p) ∪ C(p) in T , ⊲ Zeit: O(log |T |) und sr deren rechter Nachbar in T . ⊲ Zeit: O(log |T |) SucheNeuesEreignis(s′′ ,sr ,p) falls sr existiert. ⊲ Zeit: O(log n) 19: 20: 21: 22: 23: Mit m(p) := |L(p) ∪ U (p) ∪ C(p)| ist also der Zeitaufwand von BehandelEreignispunkt(p) in O(m(p) log n). Ist m die Summe aller m(p), so ist die Laufzeit aller Aufrufe zusammen in O(m log n). Ist k die Größe der Ausgabe (Anzahl ausgegebener Punkte plus jeweils der Anzahl der ausgegebenen zugehörigen Strecken), so folgt leicht m ∈ O(n + k). Aber es gilt auch m ∈ O(n + I), was aus der Betrachtung des ebenen Graphen folgt: Die ne Knoten werden von allen Schnitt- und Endpunkte gebildet, ne ≤ 2n+I. Die nk Kanten des ebenen Graphen sind dann die ungeteilten Abschnitte der gegebenen Strecken. Die Eulersche Polyederformel für alle Zusammenhangskomponenten liefert, da die Anzahl nf der Flächen maximal 2nk /3 ist (doppeltes Abzählen der Flächen-Kanten-Inzidenzen), für planare Graphen ohne Zweiecke“ die Beziehung nk ≤ 3ne − 6. Genauer: ne + nf − nk ≥ 2, ” damit nk ≤ ne + nf − 2 ≤ ne + 2nk /3 − 2. Nun ist m(p) gleich dem Grad des Knotens p. Damit ist m die Summe der Grade aller Knoten, m = 2nk ≤ 6(2n + I) − 12 < 12(n + I). Zusammen ergibt dies die gewünschte Abschätzung. ¤ Satz 3.6 Die I Schnittpunkte von n Strecken können mit einem Algorithmus (Modifikation von Algorithmus 3.3) mit Laufzeit in O((n + I) log n) und Speicherbedarf in O(n) berechnet werden. 3.2 Die doppelt verkettete Kantenliste Datenstruktur 3.7 (Doppelt verkettete Kantenliste) Eine doppelt verkettete Kantenliste besteht aus 3 Datenlisten, je eine für Knoten, Flächen und Halbkanten. • Der Datensatz zu einem Knoten vi besteht aus den Feldern Knoten = vi , aus Koordinaten = f (vi ) ∈ R2 , den Koordinaten des eingebetteten Punktes, und InzidenteHalbkante, einem Verweis auf eine (beliebige) Halbkante mit Anfang vi . • Für Flächen fi besteht der Datensatz aus den Feldern: Fläche = fi , Außenrand, eine Referenz auf eine Halbkante des Außenranden und Innenränder, eine Menge von Referenzen auf je eine Halbkante jeden Loches. • Für Halbkanten hi gibt es die Felder Halbkante = hi , Anfang, eine Referenz auf den Anfangsknoten, Gegenüber, die inverse (gegenläufige) Halbkante, Algorithmische Geometrie, WS 2009/10 12 TUM, M9, Dr. Nico Düvelmeyer 3 SCHNITTPUNKTE VON STRECKEN InzidenteFläche, die links liegende Fläche Nachfolger, die nachfolgende benachbarte Halbkante im Rand der links liegenden Fläche, und Vorgänger, die vorausgehende benachbarte Halbkante im Rand der links liegenden Fläche. Dazu können jeweils noch weitere Attribute hinzukommen. Eine doppelt verkettete Kantenliste ist gültig, wenn sie tatsächlich der Aufteilung der Ebene durch einen ebenen Graphen entspricht. 3.3 Berechnung der Überlagerung zweier Ebenenaufteilungen Die Überlagerung zweier Ebenenaufteilungen ist eine neue Ebenenaufteilung, erzeugt durch die entsprechenden aufgeteilten Originalkanten. Auftrag 3.8 Gegeben: Zwei doppelt verkettete Kantenlisten für Ebenenaufteilungen S1 , S2 . Gesucht: Doppelt verkettete Kantenlisten für Überlagerung Ü(S1 , S2 ) von S1 und S2 . Als Attribute der Flächen sollen Paare der ursprünglichen Attribute gebildet werden. Ein Großteil der Daten kann weiterverwendet werden, z.B. unzerschnittene Halbkanten. Wir betrachten ein neues Gleitebenenverfahren, jetzt wird neben T und Q auch eine doppelt verkettete Kantenliste D verwaltet. Anfangs ist D die Vereinigung von Kopien der Ausgangsdaten. Wir lassen in unserer Betrachtung erstmal Flächendaten weg! Wir müssen zudem Querverweise zwischen den Halbkanten in D und den Strecken in T verwalten. Invariante unseres Verfahrens: alles über der Gleitgerade von D ist bereits korrekt berechnet! Bei Ereignispunkten: T und Q werden wie gehabt aktualisiert. Waren in einem Schritt nur Strecken von einer der beiden Eingangsaufteilung beteiligt, so ist nichts weiter zu tun. Ansonsten sind weitere lokale Operationen nötig, um die Kantenverknüpfung neu durchzuführen. Beispiel 3.9 Kante k von S1 geht durch Knoten v von S2 . k wurde bisher durch 2 Halbkanten dargestellt, daraus werden jetzt vier! Bisherige Enden von k sind leicht korrigiert. Im Punkt v muss die exakte zyklische Position bestimmt werden und dann entsprechend die Verknüpftung durchgeführt werden. Zeitaufwand für die Korrektur von k beim Ereignispunkt v: O(m(v)), wobei m(v) weiterhin die Anzahl von Originalkanten ist, die zu v inzident sind (für die Suche der richtigen Positionen in der zyklischen Reihenfolge). Der Gesamtzeitaufwand für Halbkanten- und Knotenrekords ist in O((n + k) log n), wobei n Summe der Komplexitäten von S1 und S2 , sowie k die Komplexität vom Ergebnis bezeichnet. Dabei werden die Knoten- und Halbkantendatensätze korrekt berechnet bis auf das Datenfeld InzidenteFläche. Algorithmische Geometrie, WS 2009/10 13 TUM, M9, Dr. Nico Düvelmeyer 3 SCHNITTPUNKTE VON STRECKEN 3.3.1 Flächendaten Wir bauen die Flächendatenliste komplett neu auf, basierend auf Zyklen von aufeinanderfolgenden Halbkanten. Für Halbkantenzyklen müssen wir innere und äußere Ränder der Flächen unterscheiden! Dies gelingt z.B. durch lokale Untersuchungen am global am weitestens links liegenden Knoten (wenn nicht eindeutig: davon der unterste) des Zyklus. Für äußere Ränder ist der orientierte Innenwinkel dort kleiner als 180◦ . Weiterhin müssen wir die inneren Ränder einer Fläche mit dem äußeren Rand verknüpfen und als eine einzige Fläche erkennen. Dazu bauen wir einen (ungerichteten, einfachen) Hilfsgraphen G auf. Jeder äußere Rand entpricht genau einer Fläche, plus zusätzlich die eindeutig bestimmte unbeschränkte Fläche. Knoten von G sind die Zyklen von Halbkanten zusammen mit einem symbolischen Knoten ∞ für das unbeschränkte Gebiet. Wir verbinden in G die Knoten x und y, wenn x für inneren Rand eines Loches steht und im folgenden Sinne y links davon liegt. Sei p der am weitesten links (genauer: links unten) liegende Knoten des Zyklus x. Liegt links von p unter allen Halbkanten zuerst eine nach unten gerichtete Halbkante von y, so werden x und y verbunden. Liegt links von p gar keine Halbkante mehr, so wird x mit ∞ verbunden. Satz 3.10 Jede Zusammenhangskomponente des Graphen G stimmt genau mit der Menge von Zyklen überein, die mit einer Fläche inzidieren. Beweis Jedes Loch von f wird zu einem weiteren Zyklus verbunden, der ebenfalls f berandet. Somit gehören alle Zyklen einer Zusammenhangskomonente zur selben Fläche. Wir zeigen noch, das jeder Zyklus der ein Loch von f begrenzt auch mit dem Außenrand von f zumindest indirekt verbunden ist. Anderenfalls gäbe es ein am weitesten links liegendes Loch x von f das nicht mit dem Außenrand verbunden ist. Betrachten wir den mit x über den linkesten Punkt verbunden Zyklus y, ergibt sich ein Widerspruch. ¤ Nach dem Gleitebenendurchlauf kann in Zeit O(n+k) die Flächenverknüpfung der doppelt verketteten Kantenliste berechnet werden. Speichern wir zusätzlich noch von allen Knoten die zugehörigen Seiteninformationen während des Ebenendurchlaufs, können wir auch die Attribute der neuen Flächen allein durch lokale Betrachtungen effizient bestimmen. Dies erfordert eine Erweiterung der Informationen, die in T gespeichert werden, und deren Aktualisierung an den Ereignispunkten. Algorithmus 3.11 Eingabe: Zwei doppelt verkettete Kantenlisten für Ebenenaufteilungen S1 , S2 . Ausgabe: Doppelt verkettete Kantenlisten D für Überlagerung Ü(S1 , S2 ). Als Attribute der Flächen sollen Paare der ursprünglichen Attribute gebildet werden. 1: Funktion KartenÜberlagerung(S1 , S2 ) 2: Kopiere die Datenstrukturen für S1 und S2 in eine neue gemeinsame doppelt verkettete Kantenliste D. 3: Berechne alle Schnittpunkte zwischen Kanten von S1 und S2 mit dem Gleitebenenverfahren SucheSchnittpunkte in Algorithmus 3.3. Zusätzlich zu den Änderungen an T und Q ist bei den Ereignispunkten folgendes zu tun: Algorithmische Geometrie, WS 2009/10 14 TUM, M9, Dr. Nico Düvelmeyer 3 SCHNITTPUNKTE VON STRECKEN • Aktualisiere D entsprechend den lokalen Änderungen (siehe Beispiel 3.9) falls Kanten von sowol S1 und S2 beteiligt sind. • Speichere die Halbkante l(p) direkt links vom Ereignispunkt p zusätzlich in der Repräsentation von p in der Datenstruktur D. ⊲ Nun ist D bis auf Seiteninformationen korrekt Bestimme die Randzyklen von Ü(S1 , S2 ) beim Durchlaufen (Tiefensuche) von D. Baue den Graph G mit Knotenn entsprechend den Randzyklen und Kanten, die jedes Loch mit dem linken Nachbarn verbinden. Verwende dafür l(p). Bestimme die Zusammenhangskomponenten von G. 6: Für alle Zusammenhangskomponenten K von G mache 7: C ← der eindeutige äußere Randzyklus von K. Lege einen neue FlächenDatensatz für die zugehörige Fläche f an. Setze Außenrand(f ) auf eine Halbkante von C, und füge in Innenränder(f ) für alle Löcher in K je eine Halbkante ein. Durchlaufe alle diese Randzyklen und setze alle Referenzen InzidenteFläche auf f. 8: Bestimme die Attribute aller neuen Flächen f durch lokale Informationen an jeweils einer Ecken von f . 4: 5: Satz 3.12 Sei S1 eine Ebenenaufteilung der Komplexität n1 , S2 eine solcher der Komplexität n2 , und n := n1 +n2 . Die Überlagerung von S1 und S2 kann in Zeit O(n log n+k log n) berechnet werden, wobei k die Komplexität der Überlagerung ist. Folgerung 3.13 Für zwei ebene Polygone P1 , P2 mit n1 bzw. n2 Ecken kann P1 ∪ P2 , P1 ∩ P2 und P1 \ P2 jeweils in Zeit O((n + k) log n) berechnet werden, wobei n := n1 + n2 und k die Komplexität des Ergebnisses ist. Bemerkung 3.14 Eine untere Schranke für den Worst-Case-Zeitaufwand der allgemeinen Schnittpunktbestimmung ist Ω(n log n + k). Aber es gibt auch O(n + k)-Algorithmen, die ausnutzen, dass zwei gegebene Ebenenaufteilungen jeweils zusammenhängend sind. Algorithmische Geometrie, WS 2009/10 15 TUM, M9, Dr. Nico Düvelmeyer