亚线性但简单的动态凸壳算法?
Posted
技术标签:
【中文标题】亚线性但简单的动态凸壳算法?【英文标题】:Sublinear but simple Dynamic Convex Hull algorithm? 【发布时间】:2012-03-13 16:32:26 【问题描述】:我需要解决动态凸包算法问题,即维护2D点的凸包,我可以在其中添加和删除点。
幼稚的做法显然是O(N)
;每当添加/删除N
点之一时,我们都会从头开始重新计算凸包。但是,我负担不起线性时间,所以我正在寻找一种次线性算法。到目前为止,我找到了一堆论文,所有这些论文都描述了一些具有疯狂时间界限的复杂算法,这些算法需要很长时间才能实现。即使是最古老的高效算法,由于 Overmars 和 Leeuween,O(log^2 N)
似乎也太复杂了。 (像往常一样,此类论文中描述的大多数算法在结构/算法方面与其他参考论文有大量依赖关系)
我正在寻找更简单但不一定新颖的东西,它在最坏的情况下比线性表现更好(例如O(sqrt N)
)。最后,我不介意时间是否摊销。有什么想法吗?
(简单来说,我主要是指不需要超过几百行代码的东西。)
【问题讨论】:
我不会说线性复杂度解决方案是幼稚的,因为在线性时间内找到 N 个点的凸包是幼稚的。事实上,我不知道这种算法可以在线性时间内解决单个集合的问题。 izo 是正确的:有一个 Omega(n log n) 下限(在最常见的计算模型下)。O(N)
,我的意思是每次操作的成本。天真的方法是在每个步骤中(在每次删除/插入之后)保持点的排序并在O(N)
中进行格雷厄姆扫描。
【参考方案1】:
我认为你所寻求的并不存在。 Overmars 和 vanLeeuwen 算法并没有那么复杂,在某种意义上它似乎是必要的。首先,将问题改为分别维护上船体和下船体。其次,通过分而治之的方式构造每个,但保留中间树结构,以便您知道 1/2-sets、1/4-sets 等的外壳。现在,当您删除一个点时,您仅重新计算其在树中的祖先。当您添加一个点时,您会发现它连接到哪个叶子,并再次向上重新计算到根。
我认为如果你忽略他们论文中的细节,只是按照这个高级草图,以最无脑的方式实现所有步骤,它不会很复杂,而且会是次线性的。
M. H. Overmars 和 J. van Leeuwen。 维护飞机中的配置。 J。计算。系统。科学。,23:166-204,1981。
【讨论】:
此外,除了您的回答之外,在许多情况下,理解论文的细节比实施它们更难。【参考方案2】:我假设总共有 N 个数据点,复杂的外壳由 M 个点定义。
维护一个 Convex Hull 应该比一开始就建造它更容易(而且成本更低)。
删除现有数据点
1/ If the data point in not one of the M points defining the convex hull then it won’t affect the hull so just remove it.
2/ If it is part of the convex hull then you need to adjust the hull – more on that below in point 4
添加新数据点。
3/ If a data point is inside the complex hull then it will not affect the hull.
这是一个简单的方法来测试这一点。创建船体内部的三角剖分。使用 M 个点定义的复杂船体可以三角剖分为 M-2 个三角形。然后测试该点是否位于其中一个三角形中。
4/ if a data point is outside the hull then it will become part of the hull. However, it is only affecting a local area of the hull and it is straightforward to find the new hull in a few steps. If you have already build the hull then it should be clear to you how to adjust a local part of it.
我看不出这种维护怎么可能是 O(N)
【讨论】:
观察到移除会导致船体上的O(N)
点从船体上移除。插入也是如此,这会导致O(N)
新点成为外壳的一部分。【参考方案3】:
关于 O'Rourke 教授,他对计算几何的了解比我以往任何时候都多,我不明白 OvL 的“无意识”实现(即缺乏再平衡)如何可能是亚线性的 在最坏的情况下。
幸运的是,自 1981 年以来,我们在数据结构方面取得了一些进展。特别是,由于摊销保证就足够了,splay 树 (1985) 适用于存储凸包和合并树。 Austern et al. (2003) 提出了一种很好的方法,可以将需要的额外字段的维护与复杂的平衡操作分开,这样您就可以编写一次棘手的部分。
实现伸展树的主要困难是伸展操作,甚至比插入红黑树要简单得多。一旦展开工作,展开树很容易分裂和加入。要拆分,请张开要拆分的节点并剪切其右子节点。要加入,请展开第一棵树的最右边节点,并使第二棵树成为该节点的右子节点。
【讨论】:
那么,您是说整个算法中唯一棘手的部分是实现 splay 树? 是的,我的立场是正确的:重新平衡是必要的,否则错误的序列会将树变成路径。仍然可以将重新平衡推迟到需要时,但也可以使用,如您所说,展开树。以上是关于亚线性但简单的动态凸壳算法?的主要内容,如果未能解决你的问题,请参考以下文章
题解亚瑟王 HNOI 2015 BZOJ 4008 概率 期望 动态规划
⭐算法入门⭐《动态规划 - 线性DP》简单02 —— LeetCode 53. 最大子序和