动态移动组密度近似与窗口滑动更新下的动态凸包
在计算几何领域,动态维护点集的凸包以及对移动组进行密度近似是两个重要的研究方向。下面将详细介绍相关的概念、方法和成果。
移动组的密度近似
对于移动组的密度近似,存在一种核心集(coreset),其大小仅取决于所需的近似因子,而与输入大小无关。并且,我们可以通过构建输入点的随机移位副本来进行采样,从而找到这样的核心集。
动态凸包问题概述
凸包是计算几何中的基本结构,平面上点集 $S$ 的凸包 $CH(S)$ 已经得到了广泛研究。计算 $CH(S)$ 有多种算法,不同情况下的时间复杂度也有所不同:
- 当点集无序时,有 $O(n \log n)$ 时间复杂度的算法,且该时间复杂度匹配 $\Omega(n \log n)$ 的下界。
- 输出敏感的算法时间复杂度为 $O(n \log h)$,其中 $h$ 是 $CH(S)$ 的顶点数。
- 若点集按 $x$ 坐标排序,可使用 Graham 扫描算法在 $O(n)$ 时间内计算 $CH(S)$。
动态维护 $CH(S)$ 的问题也备受关注,不同的插入和删除操作情况有不同的处理方法,具体如下表所示:
| 操作情况 | 数据结构及方法 | 时间复杂度 |
| ---- | ---- | ---- |
| 插入和删除 | Overmars 和 van Leeuwen 提出的 $O(n)$ 空间数据结构;Preparata 和 Vitter 的方法 | 每次插入和删除 $O(\log^2 n)$ 最坏情况时间 |
| 仅插入 | Preparata 的方法 | 每次插入 $O(\log n)$ 最坏情况时间 |
| 仅删除 | Hershberger 和 Suri 的方法 | 每次更新 $O(\log n)$ 摊销时间 |
| 插入和删除都允许 | Chan 的线性空间数据结构 | 每次更新 $O(\log^{1 + \epsilon} n)$ 摊销时间 |
| | Brodal 和 Jacob,Kaplan 等人的方法 | 每次更新 $O(\log n \log \log n)$ |
| | Brodal 和 Jacob 的方法 | 每次更新 $O(\log n)$ 摊销时间,$O(n)$ 空间 |
在某些特殊情况下,还有更好更简单的结果:
- 插入和删除离线时,Hershberger 和 Suri 的数据结构支持 $O(\log n)$ 摊销时间更新。
- 若更新序列随机,Schwarzkopf 和 Mulmuley 的算法支持每次更新 $O(\log n)$ 期望时间。
- Friedman 等人考虑维护简单路径 $P$ 的凸包,可支持每次更新 $O(\log |P|)$ 摊销时间。
凸包查询类型
在大多数应用中,维护 $CH(S)$ 是为了高效执行查询。查询通常分为可分解和不可分解两种类型:
-
可分解查询
:如找到 $CH(S)$ 沿查询方向的最极端顶点、判断查询线是否与 $CH(S)$ 相交、找到从查询点到 $CH(S)$ 的两条公切线等。大多数上述数据结构可以在 $O(\log n)$ 时间内处理可分解查询。
-
不可分解查询
:如找到 $CH(S)$ 与垂直查询线或任意查询线的交点。很多数据结构无法支持 $O(\log n)$ 时间的不可分解查询。例如,Chan 的数据结构修改后可支持每次不可分解查询 $O(\log^{3/2} n)$ 时间,但摊销更新时间也会增加到 $O(\log^{3/2} n)$。后来 Chan 给出的随机算法可支持每次不可分解查询期望 $O(\log^{1 + \epsilon} n)$ 时间,摊销更新时间也是 $O(\log^{1 + \epsilon} n)$。
凸包的输出操作
另一个操作是显式输出 $CH(S)$,理想情况下时间复杂度为 $O(h)$。一些数据结构可以做到这一点,如 Hershberger 和 Suri 的数据结构;但大多数其他数据结构只能以稍慢的 $O(h \log n)$ 时间输出 $CH(S)$。
窗口滑动更新下的动态凸包
本文重点研究一种特殊的插入和删除序列,即窗口滑动更新:插入的点必须在当前点集 $S$ 的所有点的右侧,删除总是发生在当前点集 $S$ 的最左侧点。
为解决该问题,可应用之前的任意点更新数据结构,但本文提出了一种新的数据结构:
- 该数据结构使用 $O(n)$ 空间,每次更新的摊销时间为 $O(1)$。
- 所有基于二分查找的标准查询在 $O(\log n)$ 时间内完成。
- 更新后,凸包 $CH(S)$ 可在 $O(h)$ 时间内显式输出。
具体结果总结如下:
1. 凸包 $CH(S)$ 可在 $O(h)$ 时间内显式输出。
2. 给定两条垂直线,$CH(S)$ 在两条垂直线之间的顶点可按 $CH(S)$ 边界顺序在 $O(k + \log n)$ 时间内输出,其中 $k$ 是顶点数。
3. 以下查询可在 $O(\log n)$ 时间内完成:
- 给定查询方向,找到 $S$ 沿该方向的最极端点。
- 给定查询线,判断该线是否与 $CH(S)$ 相交。
- 给定查询点在 $CH(S)$ 外,找到从该点到 $CH(S)$ 的两条切线。
- 给定查询线,找到其与 $CH(S)$ 的交点,或找到与该线相交的 $CH(S)$ 的边。
- 给定查询点,判断该点是否在 $CH(S)$ 内。
- 给定凸多边形,判断其是否与 $CH(S)$ 相交,若不相交,找到它们的公切线(外切和内切)。
该结果的优势在于 $O(1)$ 摊销更新时间和简单性,即使是在特殊的更新序列下,也具有重要意义。
特殊问题设置下的凸包维护
在有分割线的特殊问题设置中,设 $L = {p_1, p_2, \ldots, p_n}$ 和 $R = {q_1, q_2, \ldots, q_n}$ 是按 $x$ 坐标递增排序的点集,$L$ 中的所有点严格在已知垂直线 $\ell$ 的左侧,$R$ 中的所有点严格在 $\ell$ 的右侧。我们要维护 $L \cup R$ 的连续子序列 $S$ 的上凸包 $U(S)$,初始时 $S = L$,删除操作删除 $S$ 的最左侧点,插入操作插入 $R$ 中 $S$ 最后一个点之后的点,且删除仅发生在 $L$ 中的点。
初始化时,使用 Graham 扫描算法从 $p_n$ 到 $p_1$ 向左处理 $L$ 中的点,每个顶点 $p_i$ 关联一个初始为空的栈 $Q(p_i)$,并设置左右邻居指针 $l(p_i)$ 和 $r(p_i)$。处理 $p_i$ 时,找到当前上凸包的顶点 $p_j$,使得 $p_ip_j$ 是新上凸包的边,将 $p_i$ 压入 $Q(p_j)$,并设置 $l(p_j) = p_i$ 和 $r(p_i) = p_j$。
这个过程可以用以下 mermaid 流程图表示:
graph TD;
A[开始] --> B[初始化栈 Q(pi) 为空];
B --> C[从 pn 到 p1 处理点 pi];
C --> D[找到当前上凸包顶点 pj 使 pipj 为新边];
D --> E[将 pi 压入 Q(pj)];
E --> F[设置 l(pj) = pi 和 r(pi) = pj];
F --> G{是否处理完 p1};
G -- 否 --> C;
G -- 是 --> H[结束];
栈的作用是维护历史上凸包顶点的左邻居,以便在未来删除点时,能从当前上凸包的任意顶点向左遍历。并且,一旦 $r(p_i)$ 指针设置好,就不会再改变。
动态移动组密度近似与窗口滑动更新下的动态凸包
一般问题设置下的凸包维护
将特殊问题设置的技术扩展到一般问题设置,即处理任意的窗口滑动更新。在一般情况下,虽然没有分割线,但可以借鉴特殊问题设置中的一些思路。
我们仍然要维护点集 $S$ 的凸包 $CH(S)$,对于每次插入和删除操作,需要保证在 $O(1)$ 摊销时间内完成。插入操作时,新插入的点在当前点集 $S$ 的最右侧;删除操作时,删除最左侧的点。
具体操作步骤如下:
1.
插入操作
:
- 当插入一个新点 $p$ 时,首先检查 $p$ 是否会影响当前凸包 $CH(S)$。
- 如果 $p$ 在当前凸包内部,则凸包不变;如果 $p$ 在凸包外部,则需要更新凸包。
- 更新凸包时,从当前凸包的最右侧顶点开始,向左检查哪些边需要被删除,哪些新边需要被添加。由于新点在最右侧,这个过程可以在常数时间内完成摊销。
2.
删除操作
:
- 删除最左侧的点 $q$ 时,同样需要检查凸包是否需要更新。
- 如果 $q$ 是凸包的顶点,那么需要重新计算凸包;如果 $q$ 不在凸包上,则凸包不变。
- 重新计算凸包时,从凸包的最左侧顶点开始,向右检查哪些边需要被调整。这个过程也可以在 $O(1)$ 摊销时间内完成。
以下是插入和删除操作的时间复杂度分析表格:
| 操作类型 | 时间复杂度 | 说明 |
| ---- | ---- | ---- |
| 插入操作 | $O(1)$ 摊销时间 | 检查新点是否影响凸包,更新凸包时从最右侧开始调整 |
| 删除操作 | $O(1)$ 摊销时间 | 检查删除点是否影响凸包,更新凸包时从最左侧开始调整 |
增强数据结构以支持更多操作
在前面的基础上,我们需要增强数据结构,使其能够支持更多类型的查询和操作。
-
支持可分解和不可分解查询
:
- 对于可分解查询,如找到 $CH(S)$ 沿查询方向的最极端顶点、判断查询线是否与 $CH(S)$ 相交等,利用二分查找的特性,在 $O(\log n)$ 时间内完成。
- 对于不可分解查询,如找到 $CH(S)$ 与查询线的交点,通过优化算法,也能在 $O(\log n)$ 时间内完成。
-
输出凸包和指定区域内的顶点
:
- 凸包 $CH(S)$ 可以在 $O(h)$ 时间内显式输出,其中 $h$ 是凸包的顶点数。
- 给定两条垂直线,$CH(S)$ 在两条垂直线之间的顶点可以按 $CH(S)$ 边界顺序在 $O(k + \log n)$ 时间内输出,其中 $k$ 是该区域内的顶点数。
以下是增强后的数据结构支持的操作列表:
1. 凸包 $CH(S)$ 可在 $O(h)$ 时间内显式输出。
2. 给定两条垂直线,$CH(S)$ 在两条垂直线之间的顶点可按 $CH(S)$ 边界顺序在 $O(k + \log n)$ 时间内输出。
3. 以下查询可在 $O(\log n)$ 时间内完成:
- 给定查询方向,找到 $S$ 沿该方向的最极端点。
- 给定查询线,判断该线是否与 $CH(S)$ 相交。
- 给定查询点在 $CH(S)$ 外,找到从该点到 $CH(S)$ 的两条切线。
- 给定查询线,找到其与 $CH(S)$ 的交点,或找到与该线相交的 $CH(S)$ 的边。
- 给定查询点,判断该点是否在 $CH(S)$ 内。
- 给定凸多边形,判断其是否与 $CH(S)$ 相交,若不相交,找到它们的公切线(外切和内切)。
这个增强过程可以用以下 mermaid 流程图表示:
graph TD;
A[初始数据结构] --> B[支持可分解查询];
A --> C[支持不可分解查询];
A --> D[输出凸包];
A --> E[输出指定区域内顶点];
B --> F[优化算法到 O(log n) 时间];
C --> F;
D --> G[O(h) 时间输出];
E --> H[O(k + log n) 时间输出];
F --> I[增强后的数据结构];
G --> I;
H --> I;
应用场景
虽然窗口滑动更新的问题设置比较特殊,但仍然有很多实际应用场景。
- 矩形包围问题 :Becker 等人考虑用两个最小总面积的矩形包围平面上的 $n$ 个矩形的问题。他们的算法有一个子问题是处理动态点集,以回答关于窗口滑动更新的特定查询。使用本文提出的结果,可以将算法的空间复杂度从 $O(n \log \log n)$ 降低到 $O(n)$,而运行时间仍然是 $O(n \log n)$。
- 多边形包围问题 :Becker 等人还研究了用两个最小总面积的矩形包围一组简单多边形的问题。同样,本文的结果可以将算法的空间复杂度降低到 $O(n)$,运行时间为 $O(n\alpha(n) \log n)$,其中 $\alpha(n)$ 是逆阿克曼函数。
这些应用场景展示了该研究在实际问题中的重要性和实用性。通过优化数据结构和算法,能够在保证运行时间的前提下,降低空间复杂度,提高算法的效率。
综上所述,本文提出的窗口滑动更新下的动态凸包维护方法,在特殊的更新序列下,具有 $O(1)$ 摊销更新时间的优势,并且能够高效地支持各种查询和操作,在实际应用中具有很大的潜力。
超级会员免费看
3227

被折叠的 条评论
为什么被折叠?



