Vereinigung von Würfeln

Werbung
Rheinische Friedrich-Wilhelms-Universität Bonn
Institut für Informatik I
Rainer Herter
Volumenbestimmung
vereinigter Würfel
17. Mai 2008
Seminararbeit im Sommersemester 2008
Zusammenfassung
In dieser Ausarbeitung wird ein Algorithmus vorgestellt, welcher
das Volumen von sich (nicht zwangsweise) schneidenden, achsenparallelen Würfeln im R3 berechnet. Dabei greift der Algorithmus auf das
Verfahren des Divide & Conquer zurück und erreicht damit eine Laufzeit von O(n4/3 · log n), wobei n die Anzahl der Würfel ist.
Inhaltsverzeichnis
1 Einführung
2
2 Der Algorithmus
2.1 Grundlagen und allgemeine Vorgehensweise
2.2 Flächenberechnung innerhalb einer cell . . .
2.2.1 Die Berechnung von ψ . . . . . . . .
2.2.2 Die Berechnung von ϕ . . . . . . . .
2.3 Die Bäume T (P), T (C̃) und T (R) . . . . . .
2.3.1 Übersicht . . . . . . . . . . . . . . .
2.3.2 Verarbeitung von T (P) . . . . . . . .
2.3.3 Verarbeitung von T (C̃) . . . . . . .
2.3.4 Verarbeitung von T (R) . . . . . . .
3 Zusammenfassung
.
.
.
.
.
.
.
.
.
2
. . . . . . . . . 2
. . . . . . . . . 7
. . . . . . . . . 12
. . . . . . . . . 12
. . . . . . . . . 13
. . . . . . . . . 13
. . . . . . . . . 14
. . . . . . . . . 15
. . . . . . . . . 18
21
1
1
Einführung
In dieser Ausarbeitung wird ein Algorithmus von Agarwal, Kaplan und
Sharir vorgestellt, der das Volumen sich schneidender Würfel im R3 in Zeit
O(n4/3 · log n) berechnen kann. [1]
Dabei wird (unter anderem) die allgemeine Lage der Würfel angenommen, und dass sich die Würfel in ihrer Größe unterscheiden dürfen.
Die Problematik dieser Berechnung ist auf Victor Klee mit seinem Klee’s
”
measure problem“ zurückzuführen, der die Vereinigung von n Intervallen im
R1 in Zeit O(n · log n) durchführte und wissen wollte, ob sein Algorithmus
damit optimal wäre (was mittlerweile bewiesen wurde).
Für Würfel gleicher Größe existiert beispielsweise ein Algorithmus, der
die Volumenberechnung schon in Zeit O(n · log n) durchführt.
Diese Ausarbeitung ist in mehrere Unterkapitel unterteilt. Abschnitt 2.1
gibt eine gute Übersicht darüber, wie der Algorithmus im Allgemeinen arbeitet. Abschnitt 2.2 geht dann näher auf die Details der partiellen Flächenberechnung
innerhalb einer cell ein, während Abschnitt 2.3 die Details bei der Verwaltung der von dem Algorithmus verwendeten Bäume eingeht.
Eine Zusammenfassung der Laufzeitanalysen im Abschnitt 3 wird eine
Gesamtlaufzeit von O(n4/3 ·log n) des vorgestellten Algorithmus’ bestätigen.
Ein praktisches Anwendungsbeispiel wäre z.B. die Volumenberechnung
von komplexen Räumen.
2
Der Algorithmus
Für den präsentierten Algorithmus gehen wir davon aus, dass sich unsere n
Würfel in allgemeiner Lage befinden. Für uns soll dies bedeuten, dass keine
Ebene im R3 existiert, auf der 2 oder mehr Seiten unterschiedlicher Würfel
liegen.
Der Algorithmus bedient sich dem Prinzip von Divide & Conquer, indem
es das Volumenproblem auf ein Problem in der Ebene reduziert, und die
Flächenberechnung auf der Ebene durch Aufteilen in Teilprobleme löst.
2.1
Grundlagen und allgemeine Vorgehensweise
Betrachten wir die z-Koordinaten der Würfel. Da allgemeine Lage gefordert ist, haben wir bei n gegebenen Würfeln exakt 2n unterschiedliche zKoordinaten, nämlich für jeden Würfel sowohl Ober- als auch Unterseite. In
Zeit O(n · log n) können wir die Zahlen z1 bis zn aufsteigend sortieren. Die
Zahlen speichern wir in der Menge Zc .
Ausgehend von n achsenparallelen Würfeln im R3 , die wir in der Menge
C speichern, benutzen wir eine Sweep-Ebene Π, die von z = −∞ hoch zu
z = +∞ den Raum durchläuft und dabei bei jedem zi ein Sweep-Ereignis
2
eintritt, denn genau an den Stellen müssen Berechnungen durchgeführt und
Datenstrukturen aktualisiert werden.
Offensichtlich gilt für 1 ≤ i < 2n, dass für jedes z ! ∈ (zi , zi+1 ) sich der
Schnitt U (C) ∩ Π(z ! ) nicht ändert. U (C) sei dabei das vom Algorithmus zu
bestimmende Gesamtvolumen der Würfel, und Π(z ! ) die Sweep-Ebene an
der z-Koordinate z ! .
Definition 1 Sei ai die Fläche von U (C) ∩ Π(z ! ). Dann ist
V olU (C) =
2n−1
!
i=1
ai · (zi+1 − zi )
das zu berechnende Gesamtvolumen.
Das einzige, was der Algorithmus folglich noch erledigen muss, ist, die
(2n − 1) Stück ai ’s effizient zu bestimmen.
Der Schnitt U (C) ∩ Π(z ! ) ist die Vereinigung von einer Menge S an (2dimensionalen) Quadraten, die sich zu den Zeitpunkten z ! ∈ Zc ständig
ändern kann (aber nicht zwangsweise muss). Ein Quadrat wird hinzugefügt,
wenn unsere Sweep-Ebene auf die Unterseite, und entfernt, wenn unser
Sweep-Ebene auf die Oberseite eines Würfels trifft.
Sei AreaU (S) die Fläche der Vereinigung der Quadrate aus S.
Betrachten wir im Folgenden die xy-Projektion des R3 . Wir sehen“ un”
sere Würfel also quasi aus der Sicht unserer Sweep-Ebene und befinden uns
nunmehr nur noch im R2 .
Definition 2 Sei V ⊂ R2 diejenige Menge an Punkten unserer Würfel
dieser Projektion. Dann ist
|V | = 4n.
Definition 3 Sei B die Boundingbox von V (das kleinste achsenparallele
Rechteck, welches die Menge V beinhaltet, siehe Abbildung 1), und sei s
definiert als
s = 2 · n1/3 .
Wir unterteilen unsere Boundingbox B nun in s (nicht zwangsweise
gleichgroße) Rechtecke B1 bis Bs , welche ausgeglichen viele Punkte aus V
2/3 ) viele. Die Rechtecke B bis
einschließen, dabei aber höchstens 4n
1
s = O(n
Bs nennen wir im Weiteren slabs“.
”
Nun wird jedes slab Bi in so genannte cells“ cij unterteilt: Pro slab
”
Bi erzeugen wir s viele gleichverteilte Unterteilungen. Jede cell“ cij hat
”
1/3 ) viele Punkte aus V (Siehe Abbildung 2).
folglich höchstens 4n
=
O(n
2
s
Definition 4 Die belegte Fläche αi innerhalb eines slabs Bi , 1 ≤ i < s, ist
gegeben als
αi = AreaU (S) ∩ Bi .
3
BoundingBox B
Y
X
Abbildung 1: BoundingBox B und Koordinatensystem zur Orientierung.
slabs
BoundingBox B
cell
Y
X
Abbildung 2: Unterteilung in slabs und cells.
Damit wir die Gesamtfläche ai respektive die belegte Fläche αi in den
einzelnen slabs Bi effizient berechnen können, benötigen wir eine geeignete
Datenstruktur.
Verwendet wird hier ein Binärbaum Ti , der für jeden slab Bi angelegt
wird. Er wird uns beim Prinzip von Divide & Conquer unterstützen, um die
belegten Flächen zu berechnen und zu verwalten. Der Aufbau der Bäume
ist relativ simpel. Die Verwaltung jedoch leider nicht.
Aufgebaut ist solch ein Baum folgendermaßen:
Die Wurzel des Baumes speichert die Fläche αi wie oben definiert. Diese
ändert sich zur Laufzeit be jedem z ∈ Zc .
Jeder Knoten von Ti , die Wurzel mit eingeschlossen, ist mit einem Rechteck !v aus Bi assoziiert, welches aus 1 ≤ i ≤ s benachbarten cells cij bis cik
besteht.
4
Die Blätter des Baumes sind folglich jeweils eine einzelne cell cil . Sie sind
zusätzlich sortiert: Das linkeste Blatt v von Ti ist die unterste cell ci1 von
Bi . Für einen Knoten v mit den Kindern w und z ist !v = !w ∪ !z .
Definition 5 Sei v ein Knoten aus Ti . Die Teilmenge Sv ⊆ S ist diejenige
Menge an Quadraten, dessen Ränder sich mit !v schneiden.
Definition 6 Sei v ein Knoten aus Ti . Die Teilmenge Sv∗ ⊆ S sei die Menge
an Quadraten, die zwar !v , jedoch nicht den Vaterknoten !u komplett
enthalten.
aus Sv∗
Flaeche von Vaterknoten
aus Sv
Y
X
Abbildung 3: Sv und Sv∗ für eine cell !v . Das oberste Quadrat ist nicht aus
der Menge Sv∗ , da es auch !u komplett enthält.
Insgesamt speichern wir in einem Knoten v die Werte αv , Sv und σv =
|Sv∗ |.
Allgemein gilt:
αv = AreaU (Sv ∪ Sv∗ ) ∩ !v .
cell
Y
aus Sv
X
Abbildung 4: αv einer cell !v und 3 Elementen aus Sv .
5
Hier unterscheiden wir bei den Knoten zwischen Blättern und inneren
Knoten. Die Berechnung der Fläche für innere Knoten ist relativ einfach:
"
!v
, falls σv ≥ 1
αv =
(1)
αw + αz , falls σv = 0
wobei w, z Kindknoten von v sind.
Die Flächenberechnung des Knotens v, wenn dieser ein Blattknoten und
damit eine cell ist, ist durchaus schwieriger und wird in Unterkapitel 2
erläutert.
Mit unseren slabs, cells und den Bäumen Ti zu den slabs kommen wir
nun zu unserer Ereignis-Struktur:
Wenn wir ein Rechteck r einfügen wollen (wir also mit unserer SweepEbene auf eine Unterseite eines Würfels treffen“), suchen wir zuerst alle
”
die slabs Bi , die von dem einzufügenden Rechteck r geschnitten werden.
Es gibt 2 Fälle, die wir hier unterscheiden müssen: Zum einen kann eine
cell cij den Rand des Rechtecks r enthalten, zum anderen kann sie auch
komplett im Rechteck r enthalten sein.
Fall 1: In diesem Falle betrachten wir cells, die den Rand des Rechtecks
r enthalten.
Weiter suchen wir uns für jedes der gefundenen Bi diejenigen cells cij , die
mindestens ein Teilstück vom Rand von r enthalten. Die Suche beschränkt
sich dabei in diesem Fall nur auf Blattknoten v des Baumes Ti , für die
!v ∩ ∂r *= ∅.
Für jeden Knoten v davon fügen wir dann r in die Menge Sv ein und
aktualisieren αv entsprechend dem Verfahren, welches in Unterkapitel 2 vorgestellt wird.
Fall 2: In diesem Falle wollen wir herausfinden, welche cells komplett im
Rechteck r liegen. Für diejenigen cells ändert sich die Menge Sv∗ respektive
der Wert σv = |Sv∗ |.
Gefunden und aktualisiert werden können diese Blattknoten wie folgt:
Eine cell cij kann nur komplett im Rechteck r liegen, wenn sich in dem zum
cell dazugehörigen slab Bi kein Knoten von r befindet, Bi aber dennoch von
r geschnitten wird.
Um die gesuchten Bi zu finden, müssen wir lediglich die entsprechenden aus der zuvor gefundenen Teilmenge der Bi herausfiltern, die keinen
Knotenpunkt von r in ihrem Bereich haben.
Betrachten wir nun also diejenigen Bi ohne Knotenpunkt aus r.
Dann existiert eine cell c1 , die einen Teil der unteren Kante, und eine
cell c2 , die einen Teil der oberen Kante von r enthält. Wie bereits erwähnt,
6
entspricht jede cell in slab Bi einem Blattknoten aus Ti . Sei w der Knoten
zu c1 und z der Knoten zu c2 aus Ti . Da die Knoten sortiert sind, suchen
wir alle O(log n) Blattknoten u aus Ti , die zwischen den Knoten w und z
liegen, und folgende Bedingung erfüllen:
Sei p(u) der Vaterknoten von u. Dann:
• !u ⊆ r
• !p(u) *⊆ r
Damit haben wir alle Blattknoten u und damit wiederum alle notwendigen cells aus Bi gefunden, die komplett im Rechteck r enthalten sind.
Für jeden dieser Knoten u erhöhen wir aus gegebenem Anlass σu um 1 und
setzen den im Knoten gespeicherten Wert der belegten Fläche αu = !u .
Nachdem nun alle Blattknoten aus allen vom Rechteck r betroffenen
Bäume Ti aktualisiert wurden, müssen wir nach der Idee des Divide &
Conquer-Verfahrens bis zur Wurzel der Bäume alle Vorfahren der aktualisierten Knoten ebenfalls aktualisieren, um in der Wurzel den aktuellen
Wert der belegten Fläche pro slab Bi erhalten.
Dabei genügt es, den Wert αv entsprechend Formel 1 zu berechnen. Die
in ganz B belegte Fläche ergibt sich trivialerweise durch die Summe der
einzelnen Bi ’s:
s
!
αGesamt =
αW urzel (Ti )
i=1
2.2
Flächenberechnung innerhalb einer cell
Sehen wir uns nun genauer die Flächenberechnungen innerhalb einer cell an,
die nicht komplett in dem einzufügenden Rechteck r liegt.
Sei ! eine cell mit ! = [x0 , x1 ] × [y0 , y1 ]. O.B.d.A können wir annehmen,
dass eine cell eine größere Breite als Höhe hat. Cells, für die dies umgekehrt
ist, lassen sich analog behandeln.
Seien weiterhin die Eckpunte von ! gegeben als p0 = (x0 , y0 ), p1 =
(x1 , y0 ), p2 = (x1 , y1 ) und p3 = (x0 , y1 ) und sei S die Menge an Quadraten,
dessen Ränder ! schneiden. Innerhalb von S unterscheiden wir zwischen 5
verschiedenen Quadraten, die vorkommen können:
• Upper Rim: Eine Menge U an Quadraten, die die komplette obere
Kante von ! enthalten. Wir speichern U in einer absteigend sortierten
Liste nach y-Koordinaten. cl sei dabei die kleinste y-Koordinate der
unteren Kanten aller Upper Rims.
• Lower Rim: Eine Menge L an Quadraten, die die komplette untere
Kante von ! enthalten. Wir speichern L in einer aufsteigend sortierten
Liste nach y-Koordinaten. f l sei dabei die größte y-Koordinate der
oberen Kanten aller Lower Rims.
7
• Pillar: Eine Menge P an Quadraten, die die obere und untere Kante
von ! schneiden.
• Corner: Eine Menge C an Quadraten, die genau einen Eckpunkt von
boxempty enthalten. Von daher liegt für jeden dieser Quadrate genau
ein Eckpunt des Quadrates in !.
• Floater: Eine Menge F an Quadraten, die 2 oder 4 Punkte innerhalb
von ! liegen haben.
Corner
Pillar
p3
p2
Upper Rim
ceiling
floor
p1
p0
Lower Rim
Floater
Abbildung 5: Die 5 Typen an schneidenden Quadraten, ceiling, floor und die
Punkte p0 , p1 , p2 und p3 ,.
Die ersten 3 Typen werden dabei als long“ bezeichnet (da sie keine
”
Vertices innerhalb von ! haben), und die Corners und Floaters als short“,
”
da diese mindestens einen Vertex in ! liegen haben.
Es ist einfach, die belegende Fläche der einzelnen Typen innerhalb der
cell zu berechnen, aber die gemeinsame ist etwas komplizierter.
Daher benutzen wir die eben definierten Typen an Quadraten, um folgende Teilflächen zu definieren:
1. Zuerst betrachten wir die Fläche, die von Upper- und Lower Rims
belegt wird (A1 ).
2. Dann die Fläche, die von den Pillars, aber nicht von den Rims belegt
wird (A2 ).
3. Die Fläche, die von Corners, aber nicht von Pillars oder Rims belegt
wird (ψ).
4. Und letztlich die Fläche, die von Floaters, aber nicht von Rims, Pillars
oder Corners belegt wird (ϕ).
8
Mit diesen 4 Flächen haben wir die komplette in der betrachteten cell !
belegte Fläche α! gegeben. α! berechnet sich daher wie folgt:



Area(!)
(x
−
x
)[(f
l − y0 ) + (y1 − cl)] + (cl − f l) ·π +ψ +ϕ
1
0
αv =
'
()
*
' () *


=: A1
=: A2
, falls cl ≤ f l
, sonst
(2)
wobei π die akkumulierte Kantenlänge aller oberen Kanten der in !
vorkommenden Pillars ist (siehe Bild).
A2
π
ψ
A1
ϕ
Abbildung 6: Alle Flächenteile A1 , A2 , ψ und ϕ zur Berechnung von αv .
Die Flächen A1 und A2 sind schnell berechnet, während die Bestimmung
von ψ und ϕ aufwändiger ist.
Bevor wir zu den Details der Flächenberechnung von ψ und ϕ kommen,
müssen wir uns noch eine Definition anschauen:
Definition 7 Ein rechtwinkliges Polygon heißt staircase“, wenn es eine
”
rechtwinklige polygonale Kette gibt, die sowohl x- als auch y-monoton ist
und die Eckpunte dieser Kette mit einer horizontalen Kante eh und einer
vertikalen Kante ev verbunden werden.
Den gemeinsamen Eckpunkt von eh und ev nennen wir apex“ (siehe
”
Abbildung 7).
Um die belegte Fläche ψ und ϕ in der cell ! zu berechnen, teilen wir
die belegte Fläche zuerst in bis zu 4 dieser staircase“-Polygone P0 bis P3
”
anhand unserer Corner-Quadrate auf, wobei der apex jedes Polygons Pi der
entsprechende Eckpunt pi von ! sein soll.
Dabei kann es natürlich vorkommen, dass ein Polygon Pi einem Pj Fläche
abschneidet, wenn sich Pi und Pj schneiden. Der Schnitt dieser Flächen wird
dann entweder Pi oder Pj zugewiesen.
9
apex P3
apex P2
P2
P3
P1
P0
apex P0
apex P1
”nibble off”
Abbildung 7: Corners innerhalb einer cell und die daraus gebildeten
staircase-Polygone. Markiert ist einer der beiden nibble off’s“.
”
Nun unterteilen wir jedes der Polygone Pi in eine Menge C̃i an Rechtecken, indem wir von jedem Vertex von Pi innerhalb der cell eine vertikale
Kante ziehen, bis wir auf die horizontale Kante eh von Pi treffen (siehe
Abbildung 8).
C̃3
C̃2
C̃0
C̃1
Abbildung 8: Unterteilung in die Mengen C̃i ’s. Die Menge C̃0 besteht beispielsweise aus 3 Rechtecken.
+
Sei C̃ = 3i=0 C̃i .
Offensichtlich ist U (C̃) = U (C) ∩ !.
Die Fläche ψ erhalten wir aus Area(C̃) und dem Schnitt mit den longTypen, also den Pillars aus P und den Rims aus U und L, wobei hierfür cl
und f l ausreichen.
Zur Bestimmung von ϕ können wir wiederum C verwenden. Speichern
werden wir jedoch erstmal nur die Fläche der Floaters ohne die Corners,
da wir später genau wie bei ψ den Schnitt mit den long-Typen einfach
bestimmen können.
Dazu berechnen wir U (F ∪ !) \ U (C) und teilen die Fläche ebenfalls in
paarweise disjunkte vertikale Rechtecke, wie schon zuvor bei C̃.
10
Die Menge an daraus resultierenden Rechtecken speichern wir in der
Menge R = {R1 , . . . , Ru }. Da U (F) O(|F|) und U (C) O(|C|) Vertices haben,
hat die Menge R offensichtlich u = O(|F| + |C|) viele Elemente.
Definition 8 Wir nennen ein Rechteck r ∈ R ein Stalagtit, wenn r die
Kante ceiling schneidet, und ein Stalagmit, wenn r die Kante f loor schneidet. r kann Stalagtit und Stalagmit zugleich sein.
Definition 9 Sei die Menge R gegeben. Dann bezeichne SM die Menge der
Stalagmiten, und ST die Menge der Stalagtiten aus R.
Stalagtiten
cl
fl
Stalagmiten
Abbildung 9: Links: cell mit staircase-Polygonen und Floaters. Rechts: Daraus gebildete Menge R.
Nachdem die einzelnen Flächenteile A1 , A2 , ψ und ϕ berechnet wurden,
ist die in der cell ! insgesamt belegte Fläche komplett berechnet worden.
Doch wie ist nun die Laufzeit dieser Berechnung?
Wir erinnern uns daran, dass nur von denjenigen cells !v die belegte
Fläche berechnet werden muss, die auf dem Rand des einzufügenden bzw.
entfernenden Rechtecks r liegen. Die cells, die komplett innerhalb von r
liegen, konnten im sortierten Baum Ti in Zeit O(log n) gefunden werden.
Es kann nur max. 4 cells geben, die einen Eckpunkt von r enthalten
(also konstant viele) und höchstens 4s = 4 · n1/3 cells, die den Rand von r
schneiden. Es sind folglich O(n1/3 ) cells zu aktualisieren. Da die cells in den
Blättern des Binärbaumes Ti liegen und dieser die Höhe log n hat, können
wir die Flächen in O(n1/3 · log n) armortisierter Zeit aktualisieren. Da die
Vorfahren bis hin zur Wurzel von Ti in jeweils O(1) Zeit und damit insgesamt
in O(log n) Zeit aktualisiert werden können, ergibt dies eine Gesamtlaufzeit
von O(n1/3 · log n) armortisierter Zeit.
Jetzt muss noch betrachtet werden, welche Zeit beim Einfügen bzw. Entfernen eines long-Typs (Rims oder Pillars) respektive eines short-Typs (Corners oder Floaters) innerhalb einer cell benötigt wird. Wir werden sehen,
11
dass bei der vorgestellten Datenstruktur das Einfügen / Entfernen eines
longs O(log n) armotisierte und für einen short O(µ · log n) mit µ < O(n1/3 )
Zeit benötigt wird.
Für jede cell legen wir 3 Bäume an: Für den Wert π benutzen wir einen
Baum T (P), der die Pillars P abspeichert. Für ψ wiederum verwenden wir
einen Baum T (C̃), und für ϕ einen Baum T (R). Wir könnten auch alle
3 Bäume in einem zusammenfassen, da sie sich nur im Informationsgehalt
unterscheiden, aber der Einfachheit halber betrachten wir jedoch mehrere
Bäume.
Wird in unserer cell ein Pillar-Rechteck hinzugefügt, so müssen wir T (P)
und π aktualisieren. T (R) hängt von P, C, f l und cl ab. Dies kann man sich
leicht am Bild von vorhin klar machen. Und T (C̃) hängt offensichtlich von
C̃ ab. Wie wir später in Abschnitt 2.3.3 sehen werden, hängt T (C) jedoch
auch von den Pillars ab und muss daher unter anderem auch aktualisiert
werden, wenn ein Pillar hinzugefügt wird.
2.2.1
Die Berechnung von ψ
ψ werden wir über Anfragen an T (C̃), so genannte corner-queries“, bestim”
men.
Definition 10 Sei ∆ ⊆ [y0 , y1 ] ein y-Intervall, und sei W∆ das Rechteck
[x0 , x1 ] × ∆. Dann sei ψ(∆) definiert als:
ψ(∆) = Area([U (C̃) \ U (P)] ∩ W∆ ).
Eine Anfrage von ψ(∆) bezeichnen wir corner-query“ bzw. als PSI-QUERY“
”
”
(siehe dazu Abschnitt 2.3.3).
Wenn sich f l oder cl ändert, so ändert sich auch ψ. Genauso verhält es
sich mit Änderungen an P, welches aber in der Formel selbst bereits beachtet
wird. Eine Anfrage mit ∆ = [f l, cl] ergibt also unseren neuen Wert von ψ.
2.2.2
Die Berechnung von ϕ
Entsprechend wie ψ berechnet sich ϕ durch
ϕ = Area([U (R) \ U (P)] ∩ W∆ ).
Mit der Wurzel von T (R) und den Werten f l und cl können wir ϕ in
Zeit O(1) berechnen. Wie dies möglich ist, wird in Abschnitt 2.3 ausführlich
erläutert. Insgesamt werden wir zum Aktualisieren von T (P), T (C̃) und T (R)
jeweils O(log n) Zeit brauchen.
12
2.3
Die Bäume T (P), T (C̃) und T (R)
Benötigt werden diese Bäume, um möglichst schnell die neuen Werte für
ψ und ϕ zu bestimmen, um die in einer cell belegte Fläche α! berechnet
zu haben. Dabei stellen wir als Anforderung, dass wir zum Einfügen bzw.
Entfernen eines einzelnen Quadrates nicht länger als Zeit O(log n) brauchen.
Wir werden uns zu Nutze machen, dass in einer cell nicht mehr als O(n1/3 )
viele Quadrate liegen können, um dann den jeweiligen Baum in Zeit O(n1/3 ·
log n) komplett neu aufzubauen.
Bevor die genauen Details erklärt werden, wird erstmal eine kleine Zusammenfassung davon gegeben:
2.3.1
Übersicht
Wie in Definition 10 auf Seite 12 definiert, benutzen wir T (C̃) für cornerqueries. Es gibt 2 Fälle beim Einfügen und Entfernen eines Quadrates r in
einer cell:
1. Short: Es handelt sich bei r um eine Corner oder einen Floater.
2. Long: Es handelt sich bei r um ein Rim oder ein Pillar.
Betrachten wir das Einfügen respektive das Entfernen eines shorts:
Dann müssen die Bäume T (C̃) und T (R) aktualisiert werden. T (P) muss
nicht aktualisiert werden, da kein Pillar vorliegt. Wir wissen, dass Quadrate
vom Typ short“ mindestens ein Vertex innerhalb der cell ! liegen haben.
”
Weiterhin ist nach Konstruktion von slabs und cells bekannt, dass jede !
höchstens 4n
viele Vertices haben kann. Eventuell noch einen Vertex mehr,
s2
aber der spielt in der O-Notation keine Rolle. Sei µ ≤ 4n
= O(n1/3 ) als2
so die maximale Anzahl an Vertices und damit die maximale Anzahl an
short-Rechtecken innerhalb von !.
Wenn es sich bei r um ein Corner-Quadrat handelt, r also aus der Menge
C ist, können wir daher C̃ in Zeit O(µ · log n) neu erzeugen. Dabei werden
alte Rechtecke aus C̃ gelöscht (in Zeit O(1)), und alle aktuell vorhandenen
Quadrate neu eingefügt. Da es sich um maximal µ viele Quadrate handeln
kann, kommen wir also auf die zuvor erwähnte Laufzeit von O(µ · log n).
Wenn es sich jedoch bei r um ein Floater-Quadrat handelt, so können
wir genau wie bei C̃ die Menge R und den Baum T (R) in der selben Zeit
O(µ · log n) neu aufbauen.
Zusammengenommen ist damit eine Laufzeit von O(n1/3 ·log n) gegeben,
da µ ∈ O(n1/3 ).
Betrachten wir nun das Einfügen respektive das Entfernen eines longs:
Dann handelt es sich bei diesem Quadrat r um ein Quadrat, welchen keinen
13
Vertex inerhalb der cell ! liegen hat. Dann gibt es folglich Änderungen an
π, ψ und / oder ϕ (siehe Abbildung 6 auf Seite 9).
Wenn r ein Pillar ist, so werden zuerst T (P) und dann T (C̃) und T (R)
aktualisiert. Danach folgt eine Aktualisierung der Werte π, ψ und ϕ
In beiden Fällen ändert sich etwas an der in der cell belegten Fläche α! .
Insgesamt wird dafür jedoch nicht mehr als O(n1/3 · log n) Zeit benötigt.
Kommen wir nun also zu den genauen Details.
2.3.2
Verarbeitung von T (P)
Die Verwaltung von T (P) ist relativ einfach, da wir diesen Baum lediglich
dazu benötigen, um den Wert π zu bestimmen. Auch hier wird das schöne
Verfahren von Divide & Conquer dazu verwendet. Es geht darum, die xIntervalle aller Pillars von cell ! zu berechnen. Dazu betrachten wir immer
2 Pillars bzw. 2 Vereinigungen von Pillars, und vereinigen sie im Vaterknoten
zu einem neuen Wert.
Jeden Knoten v ist verknüpft mit
• δv , das x-Intervall des Pillars bzw. das Intervall der Vereinigung von
Pillars
• !v = δv × [y0 , y1 ] ein Rechteck.
Sei Pv ⊆ P, so dass alle Elemente aus Pv das Intervall δv komplett
enthält,
+
∗
jedoch nicht das Intervall vom Vaterknoten. Sei weiterhin Pv = w Pw , wobei
w alle Nachfahren vom Knoten v sind, v selbst mit eingeschlossen.
Im Knoten v selbst speichern wir
• |Pv |
• π(v), die Länge des Teilstücks von δv , welches von den Pillars p ∈ P∗v
überlappt wird.
Die Wurzel von T (P) entspricht dann dem gesuchten Wert π.
Einfügen können wir einen neuen Pillar P wie folgt: Zuerst muss der
Knoten neu eingefügt werden und setzen π(v) = -δv -. Von diesem Knoten
aus aktualisieren wir die Vaterknoten. Da es nur log n Vaterknoten gibt,
benötigen wir hierfür also nur O(log n) Zeit.
Für den Baum T (P) definieren wir noch direkt eine pillar-length query“,
”
die wir für die Verwaltung von T (C̃) benötigen werden. Diese Anfrage gibt
uns für ein Intervall I einen Wert π(I) und arbeitet rekursiv.
Hierfür kurz ein wenig Pseudo-Code:
Die Laufzeit dieses Algorithmus ist O(log n), da der Baum eine Tiefe
von O(log n) hat und wir per Tiefensuche bis zu den Blattknoten laufen, wo
wir aktualisieren müssen (und die direkten Kindknoten der dabei besuchten
inneren Knoten).
14
Algorithmus 2.1 PI-QUERY(v, I)
// Gib Länge 0 zurück, falls I leer ist
if I = ∅ then
return 0
end if
if δv ⊆ I then
return π(v )
end if
if I ⊆ δv and |Pv | ≥ 1 then
// Länge von I zurückgeben
return |I |
end if
// Rekursiver Aufruf für linken und rechten Nachfolger
return PI-QUERY(L(v), I ∩ δL(v) ) + PI-QUERY(R(v), I ∩ δR(v) )
2.3.3
Verarbeitung von T (C̃)
Betrachten wir nun den Baum T (C̃). Dieser Baum wird ähnlich dem Baum
T (P) gehandhabt. Ähnlich
wie die Definition von P∗v definieren wir nun für
+
∗
i = 0, 1, 2, 3 C̃iv = w C̃iw , wobei mit w auch hier wieder alle Nachfahren
vom Knoten v gemeint ist, v selbst mit eingeschlossen. Weiterhin ist
C̃iv ⊂ (C ∈ C̃i ), so dass δv ⊆ IC ⊂ δp(v) .
Definition 11 Sei !v gegeben, und Pi ein staircase-Polygone, welches !v
schneidet. Dann ist Ji (v) definiert als das kleinste vertikale Segment, welches die y-Koordinaten von allen horizontalen Kanten von Pi beinhaltet.
Ji (v) ist leer oder besitzt nur ein Element, wenn !v nur eine horizontale
staircase-Kante von Pi schneidet (also nicht die horizontale Kante, die mit
einer vertikalen den apex bildet). Da es sich beim staircase-Polygon um eine
y-monotone Folge von Kanten handelt, sind die Elemente von Ji (v) aufsteigend oder absteigend in den y-Koordinaten sortiert.
Für jeden Knoten v von T (C̃) speichern wir folgende Informationen:
• ξi (v) = Area([U (C̃∗iv ) \ U (P∗v )] ∩ !v ) für i = 0, 1, 2, 3
• Ji (v)
Wenn die Menge C̃iv *= ∅ ist, dann besteht sie aus einem einzigen Element: Einem Rechteck Civ .
Definition 12 Sei das Rechteck Civ gegeben. Dann ist χiv definiert als
diejenige y-Koordinate einer horizontalen Kante von Civ , welche im inneren
von !v liegt, also entweder die obere oder untere Kante des Rechtecks.
15
Definition 13 conv(X) bezeichne das kleinste Intervall, welches X enthält.
Definition 14 Sei das Rechteck C gegeben. Dann bezeichne Ht(C) die
Höhe von C.
Für innere Knoten v sei

 0
ξi (v) =
(-δv - − π(v)) · Ht(Civ )

ξi (L(v)) + ξi (R(v))
Ji (v) =
"
, falls Pv =
* ∅
, falls Pv = ∅, C̃iv *= ∅
, sonst
[ξiv , ξiv ]
conv(Ji (L(v)) ∪ Ji (R(v)))
, falls C̃iv *= ∅
, sonst
(3)
(4)
Wie man sieht, hängt ξi (v) von π(v) ab. Wir aktualisieren, wie zuvor
erwähnt, den Baum T (C̃) also auch, wenn ein Pillar hinzugefügt wird, und
das obwohl Pillars in diesem Baum nicht gespeichert werden.
Betrachten wir also zunächst den Fall, dass ein Pillar P eingefügt oder
entfernt wird. Zunächst wurde dann der Baum T (P) aktualisiert. Nachdem
diese Aktualisierung abgeschlossen ist, können wir daher ξi (v) und Ji (v)
mit Hilfe von Gleichung 3 und Gleichung 4 aktualisieren. Dabei gehen wir
wie schon bei T (P) nach dem Verfahren vor, dass wir bei den Blattknoten
anfangen, und bis zur Wurzel alle beeinflussten Vaterknoten aktualisieren.
Wenn es sich bei den einzufügenden bzw. zu entfernenden Quadrates r
wiederum um ein Corner-Typ handelt, gehen wir anderweitig vor:
Zuerst speichern wir C ∈ C̃i als Rechteck Civ für alle Knoten v ∈ κ(IC ).
Dann können wir wieder gewohnt uns von unten nach oben durch den Baum
durcharbeiten und für die betroffenen Knoten ξi (v) und Ji (v) aktualisieren.
Da C̃iv *= ∅, wird für die Aktualisierung der einelementigen Menge keine
Rekursion benötigt. Dies kann man direkt an Gleichung 3 und Gleichung 4
sehen.
Kommen wir nun zu den Details der PSI-QUERY“, oder auch corner”
”
area query“ genannt.
Wir erinnern uns an die Definition 10 auf Seite Seite 12.
Da wir bis zu 4 disjunkte staircase-Polygone haben, ist
ψ(∆) = Area([U (C̃) \ U (P)] ∩ W∆ ).
das gleiche wie
ψ(∆) =
,3
i=0 ψi (∆)
mit
ψi (∆) = Area([U (C̃i ) \ U (P)] ∩ W∆ ).
16
Es soll an dieser Stelle genügen, die Berechnung von ψ0 (∆) zu zeigen,
da die Berechnung der anderen 3 Werte analog gehen.
Sei ∆ gegeben als
∆ = [α, β] ⊆ [y0 , y1 ].
Betrachten wir C̃0 , die Menge an Rechtecken der Corner-Quadrate, die ein
staircase-Polygon in der unteren linken Ecke von der cell ! bilden. Sei
C̃0 (∆) ⊆ C̃0 diejenigen Rechtecke aus C̃0 , dessen obere Kante im Intervall
∆ liegen. Die Rechtecke aus C̃0 (∆) sind anschaulich alle zusammenhängend,
und ihre äußere Kontur bildet ein staircase-Polygon P0 (∆), welches ein
Teilstück des Polygons P0 ist (siehe Abbildung 10).
Pillars
ψ0 (Λ)
β
∆
P0 (Λ)
RL
∆
α
RB
xL
xR
x0
xR
xL
IL
IB
Abbildung 10: Veranschaulichung der Variablen und Flächen.
Sei xL die x-Koordinate des linken Randes, und xR die x-Koordinate des
rechten Randes von P0 (∆). Seien weiterhin das Intervall IL = [x0 , xL ] und
das Intervall IB = [xL , XR ] definiert. Weiterhin definieren wir die Rechtecke
RL = IL × ∆ und RB = IB × [y0 , α].
Dann ist
U (C̃0 ) ∩ W∆ = [P0 (∆) \ RB ] ∪ RL .
Da RL und P0 (∆) nach Definition disjunkt sind, und RB ⊆ P0 (∆), gilt:
ψ0 (∆) = Area(P0 (∆) \ U (P)) − Area(RB \ U (P)) + Area(RL \ U (P))
= Area(P0 (∆) \ U (P)) − (xR − xL − π(IB )) · (α − y0 ) + (xL − x0 − π(IL ) · (β − α)
(5)
Dabei können π(IB ) und π(IL ) mit 2 pillar-length-queries (PI-QUERY)
beim Baum T (P) mit den Werten IB und IL berechnet werden (siehe Algorithmus 2.1 auf Seite 15).
17
Die Laufzeit von PSI-QUERY“ ist O(log n), aus dem gleichen Grund
”
wie schon bei T (P), nämlich die Tiefe des Baumes von O(log n) und der
angewendeten Tiefensuche.
2.3.4
Verarbeitung von T (R)
Nachdem nun die Bäume T (P) und T (C̃) in Zeit O(log n) aktualisiert werden
konnten bzw. auch die Queries diese Laufzeit aufwiesen, würde man dies für
den Baum T (R) ebenfalls erwarten. Und dem ist auch so. Diesen Baum
müssen wir aktualisieren, wenn ein Pillar oder ein Floater hinzugefügt oder
entfernt wird.
Für diesen Baum gehen wir zuerst analog zu den anderen Bäumen vor.
Für jeden Knoten v ∈ T (R) sei Rv definiert als Teilmenge von denjenigen
Rechtecken aus +
R, deren x-Projektion δv beinhaltet, jedoch nicht δp(v) . Sei
weiterhin R∗v = w Pw , wobei w alle Nachfahren vom Knoten v sind, v selbst
mit eingeschlossen (dies ist bisher alles analog zu T (P)).
Bei jedem Knoten v von T (R) speichern wir folgende Informationen:
• ϕ(v): Wie zuvor schon definiert ist hiermit die Fläche U (Rv∗ ) ∩ !v
gemeint, die von keinem Typ long bedeckt wird.
• h(v): Die Länge der von den Rechteckes aus Rv bedeckten linken Kante
von !v , ohne Upper- und Lower-Rims.
• λf (v): Länge der von Stalagmiten aus Rv∗ bedeckten floor, aber ohne
Pillars.
• λc (v): Länge der von Stalagtiten aus Rv∗ bedeckten ceiling, aber ohne
Pillars.
• f l(v): Die aktuelle y-Koordinate vom obersten floor.
• cl(v): Die aktuelle y-Koordinate vom untersten ceiling.
Wie bereits in Abschnitt 2.2.2 auf Seite 12 erklärt, dient der Baum T (R)
dazu, den Flächenwert ϕ zu berechnen. In der Wurzel von T (R) wird also
der fertig berechnete Flächenwert ϕ stehen.
Betrachten wir zunächst innere Knoten v:
Seien w und z die Kindknoten von v. Dann gelten folgende Formeln:
ϕ(v) =
"
0
ϕ(w) + ϕ(z) + (-δv - − π(v)) · h(v)
18
, falls Pv *= ∅
, sonst
(6)

 0
-δv λf (v) =

λf (w) + λf (z)

 0
-δv λc (v) =

λc (w) + λc (z)
, falls Pv =
* ∅
, falls Pv = ∅, Rv ∩ Sm *= ∅
, sonst
(7)
, falls Pv =
* ∅
, falls Pv = ∅, Rv ∩ St *= ∅
, sonst
(8)
T (R) hat immer die korrekten Werte von λc (v) und λf (v) bei allen Knoten, aber f l(v), cl(v), ϕ(v) und h(v) könnten falsch sein, da eine Aktualisierung von floor und ceiling (also von Upper- und Lower-Rims) nicht jeden Knoten von T (R) erreicht. Dies kann beispielsweise passieren, wenn
ein Lower Rim hinzugefügt wird, und dieser zwischen dem alten höchsten
Lower Rim und den unterstern Kanten von Λ liegen. Es wäre mitunter auch
zu kostenintensiv, dann alle Knoten zu aktualisieren, daher werden folgende
2 Invarianten benutzt:
• (I1) Für jeden Knoten v ∈ T (R) liegt keine y-Koordinate der horizontalen Kanten der Rechtecke aus Rv∗ zwischen f l und f l(v) bzw.
zwischen cl und cl(v).
• (I2) Unter der Voraussetzung, dass floor bzw. ceiling bei f l(v) bzw.
cl(v) liegen, hat ϕ(v) den Wert der Fläche U (Rv∗ ) ∩ !v , die nicht von
long-Typen bedeckt wird.
Die Informationen müssen auch nicht bei jedem Knoten v aus T (R)
korrekt sein. Es genügt, wenn sie bei denen korrekt sind, bei denen wir
Änderungen vornehmen, da auch hier nur von einem hinzugefügten Blattknoten bis hin zur Wurzel hochgerechnet wird. Viele Knoten im Baum
werden daher nicht berührt. Für diejenigen Knoten, dir wir aktualisieren
müssen, verwenden wir folgende 2 Funktionen, um die Werte für f l(v) und
cl(v) zu aktualisieren, bevor wir mit den anderen in v gespeicherten Werten
arbeiten.
Algorithmus 2.2 ADJUSTFLOOR(v)
ϕ(v ) = ϕ(v ) − λf (v ) · [fl − fl (v )]
if R(v ) ∩ Sm *= ∅ then
h(v ) = h(v ) − [fl − fl (v )]
end if
fl (v ) = fl
Mit dem Aufruf von diesen Funktionen haben wir die Werte f l(v), cl(v),
ϕ(v) und h(v) auf den aktuellen Stand gebracht und damit korrigiert.
19
Algorithmus 2.3 ADJUSTCEILING(v)
ϕ(v ) = ϕ(v ) − λc (v ) · [cl − cl (v )]
if R(v ) ∩ St *= ∅ then
h(v ) = h(v ) − [cl − cl (v )]
end if
cl (v ) = cl
Da auch dieser Baum beim Einfügen oder Entfernen von Pillars beeinträchtigt wird (da seine Flächen explizit mit Pillars geschnitten wird),
betrachten wir uns kurz die Vorgehensweise hierbei, da sie analog zu der
Vorgehensweise von T (P) und T (C̃) ist:
Wir arbeiten uns zuerst von der Wurzel aus nach unten durch und suchen entsprechend einen Platz, wo wir den Knoten einfügen bzw. wo wir
ihn entfernen müssen und korrigieren dabei mit Aufruf der beiden Funktionen ADJUSTFLOOR und ADJUSTCEILING die Werte der besuchten
Knoten. Sobald wir in den Blattknoten angekommen sind, laufen wir wieder
hoch zur Wurzel und aktualisieren dabei bei jedem besuchten Knoten die
Werte f l(v), cl(v), ϕ(v), λc (v) und λf (v) mit Hilfe von Gleichung 6, Gleichung 7 und Gleichung 8.
Zu guter Letzt müssen wir uns noch um das Einfügen bzw. Entfernen von
Floatern kümmern.
Auch hier laufen wir von der Wurzel zu den Blattknoten und aktualisieren
mit ADJUSTFLOOR und ADJUSTCEILING die Werte der besuchten
Knoten. Danach gehen wir für jeden besuchten Knoten, für den R ∈ Rv ist,
folgende Schritte durch:
Sei κv = δv × [f l, cl] und r = Ht(R ∩ κv ). Wenn wir R einfügen wollen:
• Setze h(v) = h(v) + r.
• Setze ϕ(v) = ϕ(v) + (-δv - − π(v)) · r.
• Setze λf (v) = -δv - − π(v), wenn R ein Stalagmit ist.
• Setze λc (v) = -δv - − π(v), wenn R ein Stalagtit ist.
Das Löschen von R geht analog, wobei λf (v) bzw. λc (v) auf 0 gesetzt
wird, je nachdem, ob ein Stalagmit oder ein Stalagtit vorliegt. Beim zurücklaufen“
”
bis zur Wurzel werden die Werte wieder gemäß Gleichung 6, Gleichung 7 und
Gleichung 8 neu berechnet.
Für dieses Verfahren wird genau wie bei T (P) und T (C̃) nur O(log n) Zeit
benötigt, da der Baum die Tiefe log n hat und wir nur die zu aktualisierenden
Knoten (und dessen direkten Kinder) besuchen.
20
3
Zusammenfassung
Wir haben n Würfel im R3 gegeben. Zur Berechnung des Volumens wird
eine Sweep-Ebene verwendet, die von z = −∞ bis z = +∞ den Raum
durchläuft. Dabei treten 2n Ereignisse auf, in denen die Schnittfläche neu
berechnet werden muss. Bei jedem dieser Ereignisse ändern sich höchstens
4 · n1/3 cells, die auf dem Rand vom einzufügenden oder zu entfernenden
Rechtecks r liegen. Cells, die im Inneren von r liegen, konnten wir mit Hilfe
von Ti und unseren slabs Bi in O(log n) Zeit bestimmen.
Wir haben gesehen, dass die in der cell belegte Fläche α! für cells !, die
auf dem Rand von r liegen, mit Hilfe unserer Baumstruktur und der Unterscheidung von Rims, Pillars, Corners und Floaters in Zeit O(n1/3 · log n)
berechnet werden kann. Die Aktualisierung der Bäume benötigt zwar lediglich eine Zeit von O(log n), jedoch sind es pro cell bis zu n1/3 Quadrate, die
die cell schneiden und für jedes dieser Quadrate muss diese Aktualisierung
durchgeführt werden.
Bei den gegebenen 2n Ereignissen folgt also eine Gesamtlaufzeit des Algorithmus von
O(2n · n1/3 · log n) = O(n · n1/3 · log n) = O(n4/3 · log n)
Zeit.
Literatur
[1] M. S. Pankaj K. Agarwal, Haim Kaplan. Computing the volume of the
union of cubes.
21
Herunterladen