扫描线划分Voronoi diagram_算法分析(英译中)
原文地址http://www.ams.org/samplings/feature-column/fcarc-voronoi
非完全一致性翻译,仅翻译部分内容并加入了一些自己的理解
传送门
Introduction(介绍)
假设你住在沙漠里,那里唯一的水源是散布在各处的几股泉水。对于每个位置,您需要确定离它最近的泉水。结果可能是一张地图,就像这里显示的那样,其中地形被划分为最接近不同泉水的位置区域。
这样的地图经常出现在各种应用程序中,并冠以许多名称。对于数学家来说,它们被称为Voronoi图。
Constructing Voronoi diagrams(暴力建立voronoi图)
在本文中,我们将假设我们有一个有限的点集合,并将看到如何有效地构建其Voronoi图。
当第一次遇到这个问题时,考虑每一对点似乎是最直接的。一个重要的事实是,如果已知两点 p p p 和 q q q ,那么连接这两点的线段的垂直平分线由两点到 p p p 和 q q q 的距离相等的点组成。
事实上,我们可能想垂直平分线的平面划分成两个区域, 其中一个区域中的点更接近 p p p,另一个区域中的点更接近 q q q,将由 p p p和 q q q划分出的更接近 p p p 的那个区域命名为 H p ( q ) H_p(q) Hp(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
pi相对较远的站点对
p
i
p_i
pi的Voronoi单元没有影响。事实上,当有大量的点时,我们会认为一个特定点的Voronoi细胞是由相对较少的其他点决定的。
如果我们在有 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成比例,这代表了一个相当大的改进。下面的图表比较了这两种算法的计算工作量根据站点的数量。