掃描線劃分Voronoi diagram_演算法分析(英譯中)
原文地址http://www.ams.org/samplings/feature-column/fcarc-voronoi
非完全一致性翻譯,僅翻譯部分內容並加入了一些自己的理解
傳送門
Introduction(介紹)
假設你住在沙漠裡,那裡唯一的水源是散佈在各處的幾股泉水。對於每個位置,您需要確定離它最近的泉水。結果可能是一張地圖,就像這裡顯示的那樣,其中地形被劃分為最接近不同泉水的位置區域。
Constructing Voronoi diagrams(暴力建立voronoi圖)
在本文中,我們將假設我們有一個有限的點集合,並將看到如何有效地構建其Voronoi圖。
當第一次遇到這個問題時,考慮每一對點似乎是最直接的。一個重要的事實是,如果已知兩點 p p p 和 q q q ,那麼連線這兩點的線段的垂直平分線由兩點到 p p p 和 q q q 的距離相等的點組成。
事實上,我們可能想垂直平分線的平面劃分成兩個區域, 其中一個區域中的點更接近
p
p
p,另一個區域中的點更接近
q
q
構建Voronoi圖似乎相當簡單:要找到比任何其他站點更接近
p
i
p_i
pi的點集合,我們只需取所有半平面
H
p
i
(
p
j
)
{H} {p_i}(p_j)
Hpi(pj)的交集。也就是說,包含站點
p
i
p_i
pi的Voronoi單元格由
∩
j
H
p
i
(
p
j
)
\cap_j {H}_{p_i}(p_j)
∩jHpi(pj)給出。
當然,正如上圖所示,我們在這裡所做的一些工作是不必要的;也就是說,一些距離
p
i
p_i
如果我們在有 n n n 個點時使用此方法,那麼我們需要考慮其他 n − 1 n -1 n−1 個點來查詢該點的Voronoi單元格。由於每個站點都有自己的Voronoi單元格,我們需要構造 n n n 個Voronoi單元格,這將共需要構造 n ( n − 1 ) = n 2 − n ≈ n 2 n(n-1)=n^2-n\approx n^2 n(n−1)=n2−n≈n2 ( n n n個單元格,每個需要其餘 n − 1 n-1 n−1 個點來分別求取 p i p_i pi與 p j p_j pj之間的半平面)半平面。例如,如果我們有1000個點,我們將需要構建大約一百萬個半平面。
看來,實現這個方法將需要我們做很多工作,其中很多是不必要的。一個自然的問題是,是否有一種更有效的方法來計算Voronoi圖。
Fortune’s algorithm(掃描線演算法)
事實上,有幾種方法可以找到Voronoi圖表,其中一種被稱為 F o r t u n e ′ s a l g o r i t h m Fortune's algorithm Fortune′salgorithm。
該演算法基於以下聰明的想法:我們將引入一條通過平面移動的直線,而不是考慮不同地點之間的距離,並利用這條直線進行更有效的距離比較。
我們稱這條線為掃線並認為它在掃過平面時揭示了Voronoi圖。
讓我們首先記住,如果我們已知一個點 p p p和一條直線 l l l(不包含 p p p),那麼到 p p p的距離與到 l l l的距離相等的點構成了一個拋物線。
我們將使用 P p , l P_{p,l} Pp,l來表示這個拋物線。和之前一樣,是拋物線將平面劃分成兩個區域:一個點組成的接近 p p p等組成的點接近 l l l。
這是很重要的一點,讓我們講清楚一點。考慮一個點 q q q,它的座標是 q = ( q x , q y ) q=(q_x,q_y) q=(qx,qy),它到 p p p的距離表示為 d ( q , p ) d(q,p) d(q,p)。接下來,掃描線是一條水平線:我們稱它的縱座標為 l y l_y ly,所以 q q q 和 l l l 之間的距離是 q y − l y q_y-l_y qy−ly。因此,位於拋物線 P p , l P_{p,l} Pp,l上的點 q q q具有該關係: d ( q , p ) = q y − l y d(q, p) = q_y-l_y d(q,p)=qy−ly。
更普遍來說
d
(
q
,
p
)
<
q
y
−
l
y
d(q, p) < q_y-l_y
d(q,p)<qy−ly 若
q
q
q 在
P
p
,
l
P_{p,l}
Pp,l 上方 ,
d
(
q
,
p
)
=
q
y
−
l
y
d(q, p) = q_y-l_y
d(q,p)=qy−ly 若
q
q
q 在
P
p
,
l
P_{p,l}
Pp,l 上,
d
(
q
,
p
)
>
q
y
−
l
y
d(q, p) > q_y-l_y
d(q,p)>qy−ly 若
q
q
q 在
P
p
,
l
P_{p,l}
Pp,l 下方.
現在水平掃描線
l
l
l將通過平面向下移動。
在任何時候,我們將只考慮位於掃描線和由這些點定義的拋物線
P
p
,
l
P_{p,l}
Pp,l之上的站點。
例如,給定的集合網站和掃線的位置在第一張圖片所示,我們將考慮第二張所示的拋物線上方的點。
現在我們可以介紹
F
o
r
t
u
n
e
′
s
a
l
g
o
r
i
t
h
m
Fortune's algorithm
Fortune′salgorithm中的真正關鍵點:令海灘線為最低的拋物線弧所形成的曲線。即為下圖中黃色的曲線(這裡在原文中有更詳細的說明,但因為我水平有限,無法很好的翻譯…QwQ)
海灘線非常適合構建Voronoi圖。例如,如果一個點位於海灘線之上,它顯然更接近
l
l
l以上的其中一個站點而不是
l
l
l。
這意味著這一點位於掃線已經穿過的Voronoi單元格中。因此,海灘線以上的Voronoi圖是由掃線以上的站點決定的。
現在我們來確定海灘線何時通過任意點 q q q。假設 q q q在所有站點中與 p 1 p_1 p1最接近;即: d ( q , p 1 ) ≤ d ( q , p i ) d(q,p_1)\leq d(q,p_i) d(q,p1)≤d(q,pi) ( p i p_i pi為所有其他站點), q q q位於 P p 1 , l P_{p_1,l} Pp1,l上的條件可以表示為 d ( q , p i ) ≥ d ( q , p 1 ) = q y − l y d(q, p_i)\geq d(q,p_1) = q_y-l_y d(q,pi)≥d(q,p1)=qy−ly
這意味著當
q
q
q出現在
P
p
1
,
l
P_{p_1,l}
Pp1,l上時,它不能在另一個拋物線
P
p
i
,
l
P_{p_i,l}
Ppi,l的上方。因此,當
q
q
q出現在拋物線
P
p
1
,
l
P_{p_1,l}
Pp1,l上時,它在海灘線上。
讓我們總結一下這個重要的觀察結果:
當一個點出現在海灘線上時,它是在與其最近站點相關的拋物線上。
海灘線上位於兩個拋物線弧上的點稱為斷點。應用我們的觀察,可以發現斷點離兩個點最近。換句話說,斷點位於Voronoi圖的邊緣。
這意味著當掃描線沿平面向下移動時,斷點將掃描Voronoi圖的邊緣。因此,要構建Voronoi圖,我們只需要跟蹤斷點。
Finding the edges(找邊)
到目前為止,我們已經看到,如果我們想要構建Voronoi圖的一條邊,我們只需要在掃描線沿平面向下移動時跟蹤對應的一對斷點。執行此操作的第一步是檢測何時構造了一對斷點。如下圖所示,當一個新的弧線被新增到海灘線時,就會發生這種情況。
換句話說,當掃線經過一個站點時,對應於Voronoi圖中的一條邊的一對斷點就會出現在海灘線上。我們將這個事件稱為 點事件
Finding the vertices(找點)
當掃描線向下移動時,斷點沿著Voronoi圖的邊緣連續移動,直到到達圖的頂點。當沿海灘線的拋物線消失時,就會發生這種情況。在接下來的一系列圖片中,請注意圖中從右邊開始的第二個拋物線弧(最小的弧)消失了。
在海灘線上出現的新的拋物線很容易被探測到:當掃線經過一個地點時,它們就會出現。
同樣,拋物線弧的消失也很容易檢測到。當一條拋物線收縮到一點
q
q
q時,這個點就位於三個拋物線上:一個包含消失的弧線,另一個包含兩邊的弧線。這意味著
q
q
q到三個點的距離是相等的,這三個點對應於這些拋物線,因此這三個點位於以
q
q
q為圓心的圓上。因此,當掃線經過那個圓的底部時,我們找到頂點。
我們稱這樣的事件為 圓事件
請注意,圓事件還建立了一條新邊。這與我們有一個新的斷點形成有關,這是兩個拋物線弧的交點,它們是與消失的弧相鄰的。
The algorithm(演算法)
我們現在已經具備了理解
F
o
r
t
u
n
e
’
s
a
l
g
o
r
i
t
h
m
Fortune’s algorithm
Fortune’salgorithm所需的一切知識。要檢測圖中的邊和頂點,只需找到海灘線上拋物線的出現和消失即可。
因此,我們將通過想象沿著海灘線從左到右走,記錄產生拋物線的地點的順序來跟蹤海灘線。我們知道這個順序不會改變,直到掃線到達一個站點事件或迴圈事件。此外,通過注意海灘線上拋物線弧的鄰接,可以隱式地記錄斷點。
如果掃描線遇到的下一個事件是站點事件,我們只需按照相應的拋物線出現在海灘線上的順序將新站點插入到站點列表中。然後我們記錄我們遇到一個新的邊緣圖。
如果掃線遇到的下一個事件是一個圓事件,我們記錄下我們在圖中遇到了一個頂點的事實,並且這個頂點是連線到一起的兩個斷點對應的邊的端點。我們還記錄了圓事件產生的新斷點對應的新邊。
無論我們遇到一個點或圓事件,我們總是會檢查我們是否在海灘線上增加了可能導致未來圓形事件的三個新的拋物線弧。如果導致未來圓事件的三重拋物線弧在事件之後不再存在,我們也有可能將未來圓事件從考慮中移除。(檢查新事件,去除失效的舊事件)
這樣,考慮事件的有限序列就構成了圖。下面顯示的是計算所示站點集合的Voronoi圖的事件序列。圓圈事件用綠點表示。
Summary(總結)
F
o
r
t
u
n
e
’
s
a
l
g
o
r
i
t
h
m
Fortune’s algorithm
Fortune’salgorithm是一種非常有效的方法來計算泰森多邊形法圖。無論我們使用哪種演算法,我們都應該預期擁有更多的站點將需要更多的工作來找到Voronoi圖。然而,我們可以更精確地說明這一點。早些時候,我們考慮了一種找到Voronoi圖的演算法,通過交叉包含站點的每個半平面來找到每個Voronoi單元。如果
n
n
n是站點的數量,則實現該演算法所需的步數與
n
2
log
n
n^2\log n
n2logn成比例。然而,對於一個典型的圖表,實現
F
o
r
t
u
n
e
’
s
a
l
g
o
r
i
t
h
m
Fortune’s algorithm
Fortune’salgorithm所需的步驟數量與
n
log
n
n\log n
nlogn成比例,這代表了一個相當大的改進。下面的圖表比較了這兩種演算法的計算工作量根據站點的數量。