假设我有100万个任意形状,任意定向的N维椭圆体随机散布在N维空间中。给定一组子椭球,我想“快速”确定第一组椭圆体相交的所有椭球的集合。
必须有一个算法。它是什么?什么是“O”复杂性?
答案 0 :(得分:6)
如果您允许N维数据,则“O”复杂性会受到维度诅咒的影响。 (有关详细信息,请参阅this wikipedia article)。我建议借用物理模拟并将这个问题分为“广泛阶段”和狭窄阶段:
窄阶段是测试任意椭圆之间的交叉的直接计算几何问题。对于广义阶段,您需要使用空间结构,例如空间散列,空间树(R树,Kd树,X树,UB树等等),或者给定ad-hoc结构您正在加载的数据的某些特殊属性(例如不平衡的树或散列)。
目前流行的方法是Kd树。有很多文档和已经编码的Kd树版本可以很容易地配置,所以我建议你在线查看。 (谷歌是你的朋友。)使用大多数树结构的优点是,如果设置你正在寻找相对紧凑的交叉点,你只能在树中搜索一次并找到交叉点而不必执行多个树遍历。这将有助于缓存(无论是来自主内存还是磁盘)访问模式。相同的算法可以处理不同的成员查询。但是,您可能正在从紧凑的查询集属性中获益很多。
Kd树不会修复所有椭圆体的问题 - 例如,如果你有一个尺寸为N的椭圆体,其主轴是从(0,0,0,0,...)到(0) ,1,1,1,......)但是,由于次要轴较小或无关紧要(以后不会相交很多),仍需要在所有N维中覆盖[0,1]的节点。如果您的椭圆体落在[0,1] ^ n中,那么每个椭圆体将测试与上述不方便的椭圆体的交点。然而,对于现实世界的数据(甚至大多数合成数据,除非你真的努力使Kd树变慢),Kd树方法应该是一个胜利。
如果你期望Kd-tree在千维椭圆体上取得成功,你可能会更好地进行强力搜索。 (上面提到的维数诅咒。)但是......
如果你有一个优化的实现,一百万个条目也不算太糟糕,但是如果你做了很多查询(数百万),它会变慢(大约10秒或更差)。然而,我已经看到一些令人惊讶的数字来自经过优化的矢量化代码。 (甚至使用这种策略运送了一些产品。)通过正确的缓存一致性,强制执行最多只需要几毫秒。这意味着在C / C ++中使用ASM或矢量内在函数 - 不确定您正在使用哪种语言。
对于大多数数据,O复杂度(忽略维度的诅咒)应该是关于查询(一旦构建树)的摊销O(m log n),其中m是查询集中的省略号数,n是数据集中的省略号数。构建数据本身不应该比O(n log n)差。将所有内容乘以Exp(d),其中d是维度 - 这就是它与这种事物的关系。