Theorie und Praxis geometrischer Algorithmen

Werbung
Theorie und Praxis
geometrischer Algorithmen
Sweep Line Algorithmus
Roman Adam
Einführung
Gegeben: Liniensegmente si, dargestellt durch ihre linken und rechten
Endpunkte li und ri mit 0  i  n.
Man betrachtet die Endpunkte und Schnittpunkte als Knoten
eines Graphen und die Segmentabschnitte zwischen diesen
Punkten als Kanten.
Gesucht ist ein Algorithmus, der die Schnittpunkte berechnet
und aus den Punkten und Segmenten einen Graphen aufbaut.
Konstruktion eines planaren Graphen:
k
v
L
a
R
v`
k`
Man speichert die Kante a in einer speziellen Form ab:
k
previous
from
to
left
right
v
v`
L
R
next
k`
Einfach-Lösung:
Alle Segmente paarweise auf Schnittpunkte testen,
gefundene Schnittpunkte speichern und dann Graph
berechnen.
Problem:
schlechte Laufzeit: O(n2)
•
•
nicht besonders effizient, falls wenige Schnittpunkte
man möchte den Graphen während der SchnittpunktBerechnung konstruieren
gesucht:
Algorithmus, der nicht nur von der Anzahl der
Segmente, sondern auch von der Anzahl der
Schnittpunkte abhängt
 Output - sensitiv
Idee: Man braucht nur Segmente zu testen, die
‚nah‘ beieinander liegen.
Aber was ist ‚nah‘?
Wir projezieren die Segmente auf die x-Achse
y
x
Man sieht:
Durch Projektion der Segmente auf die x-Achse erhält
man einen Hinweis, ob zwei Segmente ‚nah‘
beieinander sind.
Idee:
Man bewegt eine (imaginäre) Linie parallel zur y-Achse von
links nach rechts.
 Sweep Line („Fegelinie“)
Man betrachtet nur die Segmente, die an einer bestimmten
Stelle x diese Linie schneiden und testet die Segmente auf
Schnitt, die nebeneinander liegen.
 Aus einem statischen zweidimensionalen wird ein
dynamisches eindimensionales Problem.
Frage:
Wie findet man heraus, welche Segmente die Sweep Line
schneiden?
y
x
• Die Sweep Line muss nur an diskreten Punkten, den
Endpunkten der Segmente ‚anhalten‘.
• Man betrachtet die x-Achse als Zeitachse und bezeichnet
einen solchen diskreten Punkt als Ereignis.
Diese Ereignisse werden in einer besonderen Datenstruktur
gespeichert, der Ereignis- oder X-Struktur.
Diese Datenstuktur wird also zu Beginn mit den Endpunkten
der Segmente initialisert, die in aufsteigender Reihenfolge
nach ihren x-Koordinaten geordnet sind.
Wie werden die Segmente behandelt, die die Sweep Line
schneiden?
Die Segmente, die zu einem bestimmten Zeitpunkt die Sweep
Line schneiden, werden in der Status- oder Y-Struktur
gespeichert und zwar in aufsteigender Reihenfolge ihrer yKoordinaten.
Die Y-Struktur ist zu Beginn leer.
x1: s3, s1, s4, s2
x4: s3, s2,s5
x2: s3, s4, s2
x5: s2, s3,s5
x3: s3, s2
x6: s2, s5,s3
y
s5
s2
s4
s1
s3
x
x1
x2
x3
x4
x5
x6
Sweep Line Algorithmus
Voraussetzung:
• x-Koordinaten der Schnitt- und Endpunkte sind
paarweise verschieden
• Länge der Segmente > 0
-
nur echte Schnittpunkte
keine Linien parallel zur y-Achse
keine Mehrfachschnittpunkte
keine überlappenden Segmente
Beim Sweep können folgende Ereignisse eintreten, die die
Ordnung der Segmente in der Y-Struktur verändern und
eine Aktualisierung der X-Struktur erfordern:
(1) Die Sweep Line stösst auf den linken Endpunkt eines
Liniensegments
(2) Die Sweep Line stösst auf den rechten Endpunkt eines
Liniensegments
(3) Die Sweep Line stösst auf den Schnittpunkt zweier
Liniensegmente
Frage:
Wie soll man die Schnittpunkte kennen, wenn man sie
doch suchen soll?
y
s5
s2
s4
s1
p
s3
x1
x2-
x2
x
•
kurz vor einem Schnittpunkt sind die beiden Segmente
benachbart
• durch die Ordnung in der Y-Struktur kann man dies leicht
erkennen
 Man muss zwei Liniensegmente sofort auf Schnitt testen,
wenn sie in der Y-Struktur Nachbarn werden.
 Durch diesen Test, kann man alle Schnittpunkte
spätestens kurz vor ihrem Auftreten erkennen.
Lemma 1:
Wenn zwei Liniensegmente einen echten Schnittpunkt haben,
wo werden sie unmittelbar vorher direkte Nachbarn in der
Ordnung längs der Sweep Line.
y
s5
s2
s4
U(p)
s1
p
s3
x1
x2-
x2
x
Beweis:
Sei p = (x2, y2) echter Schnittpunkt von s2 und s3. Weil nach
Voraussetzung kein anderes Liniensegment den Punkt p
enthält, gibt es ein  > 0, s.d. die Umgebung U(p) nur von s2
und s3 geschnitten wird => zu jedem Zeitpunkt x  (x2 - , x2)
sind s2, s3 direkte Nachbarn in der Y-Struktur, spätestens nach
dem letzten Ereignis vor Erreichen von p.
•
Wie sind die Ereignisse zu behandeln?
Linker Endpunkt:
• neues Segment s richtig in die Y-Struktur einfügen
• s mit Vorgänger si und Nachfolger sk auf Schnitt testen
• gefundenen Schnittpunkt als zukünftiges Ereignis an der
richtigen Stelle in die X-Struktur einfügen
sk
s
si
Rechter Endpunkt:
• Segment s aus Y-Struktur entfernen
• Vorgänger si und Nachfolger sk sind jetzt selbst Nachbarn
und werden sofort auf Schnitt getestet.
sk
si
s
Schnittpunkt:
• die beiden Linien si und sk, die sich schneiden, vertauschen
ihre Position in der Y-Struktur
• der alte Vorgänger von si (sh) ist jetzt Vorgänger von sk
• der alte Nachfolger von sk (sm) ist jetzt Nachfolger von si
• somit müssen sk mit sh und si mit sm auf einen gemeinsamen
Schnittpunkt getestet werden
sm
sk
si
sh
Sweep-Line-Algorithmus
•
initialisiere die Ereignis-Struktur mit den schon bekannten
Ereignissen, den Endpunkten der Segmente, geordnet
nach aufsteigender x-Koordinate
•
initialisiere die (leere) Y-Struktur
•
nimm das erste Ereignis aus der X-Struktur und behandele
es wie angegeben
•
lies solange Ereignisse und behandle sie wie angegeben,
bis die X-Struktur leer ist
/* Initialisierung */
Init();
/* Sweep und Konstruktion des Graphen */
while( X  0 ) {
Ereignis := NächstesEreignis( X);
switch( Ereignis) {
case LinkerEP:
FügeEin(Y, Seg);
VSeg := Vorg( Y, Seg);
TesteSchnittErzeugeEreignis( VSeg, Seg);
NSeg := Nachf( Y, Seg);
TesteSchnittErzeugeEreignis( Seg, NSeg);
case RechterEP:
VSeg := Vorg( Y, Seg);
NSeg := Nachf( Y, Seg);
Entferne( Y, Seg);
TesteSchnittErzeugeEreignis( VSeg, NSeg);
case Schnittpunkt:
Vertausche( Y, USeg, OSeg);
VSeg := Vorg( Y, OSeg);
TesteSchnittErzeugeEreignis( VSeg, OSeg);
NSeg := Nachf( Y, USeg);
TesteSchnittErzeugeEreignis( USeg, NSeg);
}
}
Die X-Struktur und die Y-Struktur können als balancierter
binärer Suchbaum implementiert werden.
Aufbau: O(n log n)
insert, lookup, delete, search: O(Höhe) = O(log n)
Korrektheit:
Die Korrektheit des beschriebenen Sweep Line
Algorithmus unter den angegebenen Voraussetzungen
folgt aus Lemma 1.
Laufzeit:
Lemma: Mit dem angegebenen Verfahren kann man die k
echten Schnittpunkte von n Liniensegmenten in Zeit
O((n+k)log n) berechnen.
Beweis: Der Algorithmus beginnt mit der Konstruktion der
X-Struktur. Ist diese als balancierter binärer Suchbaum
implementiert, benötigt dies O(n log n) Zeit. Dann werden
die Ereignisse abgearbeitet. Insgesamt gibt es 2n+k
Ereignisse. Weil keines von ihnen mehrfach in der XStruktur vorkommt (das Einfügen eines schon vorhandenen
Ereignisses wird nicht erlaubt), sind niemals mehr als O(n2)
Ereignisse in der Ereignis-Struktur gespeichert. Jeder Zugriff
auf die Ereignis-Struktur ist also in Zeit O(log n2) = O(log n)
ausführbar.
Die while-Schleife wird (2n+k)-mal durchlaufen; bei jedem
Durchlauf werden konstant viele Operationen auf der X- oder
der Y-Struktur durchgeführt.

Line Sweep für degenerierte Segmente
(am Beispiel LEDA)
Um gleiche x-Koordinaten für die Ereignis-Punkte
zuzulassen, ändern wir den Aufbau der Sweep Line:
Wir definieren die Sweep Line durch einen Punkt
p_sweep = (x_sweep, y_sweep).
Die Sweep Line L ist nun ein vertikal aufsteigender Strahl
bis zum Punkt (x_sweep + 2, y_sweep + ), gefolgt von
einem horizontalen Teil bis zum Punkt (x_sweep – 2,
y_sweep + ), gefolgt von einem vertikal steigenden Strahl.
L
e
b
h
s4
s5
s7
a
i
s9
s3
s2
f
g
p_sweep
s1
s6
c
s8
d
Man beachte, dass kein Endpunkt
irgendeines Segmentes auf L liegt,
dass keine zwei Segmente L im
gleichen Punkt schneiden, ausser
sie überlappen sich und dass kein
nicht-vertikales Segment den
horizontalen Teil von L schneidet.
Aufbau X-Struktur
Die X-Struktur enthält einen Eintrag für jeden Endpunkt
rechts der Sweep Line und einen Eintrag für jeden
Schnittpunkt zwischen Segmenten, die in der Y-Struktur
benachbart sind.
Jeder Eintrag enthält zudem einen Link auf einen Eintrag in
der Y-Struktur oder nil. Ist das Ereignis ein rechter
Endpunkt, so zeigt der Link auf das zugehörige Segment.
Ist es ein Schnittpunkt, so zeigt der Link auf eines der
Segment, die sich in diesem Schnittpunkt schneiden.
Bei einem linken Endpunkt, ist der Link nil.
L
e
b
h
s4
s5
s7
a
i
s9
s3
s2
f
g
p_sweep
s1
s6
c
s8
d
X-Struktur:
<a,sit4>,<b,sit4>,<c,sit1>,<d,nil>,<e,sit9>,<f,sit1>,<g,sit2>,<h,sit3>,<i,sit1>
Aufbau Y-Struktur
Die Y-Struktur enthält neben dem Segment, dass die Sweep
Line schneidet, einen Verweis auf ein anderes Element, dass
an der gleichen Stelle die Sweep Line berührt (überlappende
Segmente) oder einen Verweis auf einen Schnittpunkt (also ein
Ereignis in der X-Struktur), falls solche vorhanden sind, sonst
nil. Die Leda-Funktion hat zudem zwei Elemente + und –
als Begrenzer der Struktur nach oben und unten.
Ordnung in der YStruktur
Die Segmente werden in
lexikographischer Ordnung sortiert. Überlappende Segmente
werden nach ihrer ID-Nummer sortiert.
L
e
b
h
s4
Y-Struktur
s5
<s3,nil>
s7
a
i
s9
s3
<s4,xita>
<s9,nil>
s2
f
<s2,nil>
g
p_sweep
<s8,xitf>
<s1,sit8>
s1
s6
c
s8
d
X-Struktur:
<a,sit4>,<b,sit4>,<c,sit1>,<d,nil>,<e,sit9>,<f,sit1>,<g,sit2>,<h,sit3>,<i,sit1>
Die Abarbeitung eines Ereignisses p, also eines Knotens geschieht nun so:
1.
2.
3.
4.
5.
6.
7.
Füge einen Knoten v an Position p zum Ausgabe-Graph.
Erstelle aus allen Segmenten in der Y-Struktur, die die Sweep Line
in p schneiden, eine Substruktur, die möglicherweise leer ist.
Füge für jedes Segment in der Substruktur eine Kante zum Graphen
hinzu.
Lösche alle Segmente, die in p enden, aus der Y-Struktur.
Aktualisiere die Ordnung in der Substruktur der Y-Liste, dabei
werden überlappende Segmente nach ihrer ID geordnet. Dies kann
als bewegen der Sweep Line über den Punkt p hinaus angesehen
werden.
Füge alle Segmente zur Y-Struktur, die in p beginnen.
Erzeuge Ereignisse für alle Segmente in der Y-Struktur, die durch
die letzten Aktionen Nachbarn geworden sind, teste also auf Schnitt
und aktualisiere die Verkettung von X- und Y-Struktur.
Y-Struktur
s1
v
s2
s4
<+,nil>
<s1,nil>
s5
s3
<s2,xitv>
<s4,xitv>
<s5,xitv>
(v,u1),(v,u2),(v,u4),(v,u3),(v,u5)
<s3,sit5>
<-,nil>
Durch die Verbindung von X- und YStruktur werden die m eingehenden
Knoten vom Leda-Sweep in O(m)
abgearbeitet.
Laufzeit:
O((n+s)log(n+m)+m)
n = Zahl der Segmente
s = Zahl der Knoten
m = Zahl der Kanten
(n+s) Schleife über alle Ereignisse
log(n+m) Operationen im Suchbaum
m Bearbeitung der Substruktur
Herunterladen