"O"复杂度在处理N维数据时会受到“维数灾难”的影响。(有关此问题的更多信息,请参见
此维基百科文章)。我建议从物理模拟中借鉴,将此问题分为“广域”和“窄域”两个阶段:
- 广域阶段保守地找到潜在重叠椭圆对的大量较小集合。
- 窄域阶段将潜在重叠椭圆对的集合修剪为实际重叠的那些椭圆对。
窄域阶段是一个简单的计算几何问题,测试任意椭圆之间是否相交。对于广域阶段,您需要使用空间结构,例如空间哈希、空间树(R树、Kd树、X树、UB树等)、或者根据您正在加载的数据的特殊属性给出的临时结构(例如不平衡的树或哈希)。
当前流行的方法是Kd树。有很多文档和已经编码好的Kd树版本可以轻松配置,所以我建议您在网上查找(谷歌是您的好朋友)。使用大多数树结构的优点是,如果您要查找的集合相对紧凑,则可以仅搜索一次树,并在不必执行多个树遍历的情况下找到交点。这将有助于缓存(无论是来自主内存还是磁盘)访问模式。同样的算法可以处理异类查询成员。然而,您正在进行的工作很可能会受益于紧凑的查询集属性。
Kd树并不能解决所有椭圆体的问题——例如,如果您有一个维度为N的椭球体,其主轴从(0,0,0,0,...)到(1,1,1,1,...),但副轴较小或不重要(因此不会相交),仍需要在覆盖所有N个维度的[0,1]节点中。如果您的椭圆体落在[0,1]^n中,则每个椭圆体都会测试与上述不方便的椭圆体的相交。但是,使用真实世界数据(甚至大多数合成数据,除非您确实努力使Kd树变慢),Kd树方法应该是胜利的选择。
如果您预计Kd树对千维椭圆体会成功,那么最好使用暴力搜索。(前面提到的维数灾难。)然而...
如果您有一个经过优化的实现,那么一百万个条目并不算太糟糕,但如果您要进行大量查询(数百万次),它将非常缓慢(约为10秒或更长)。然而,我已经看到一些出色的数字,这些数字来自于经过优化的矢量化代码。 (甚至使用了这种策略的产品。)通过正确的高速缓存一致性,暴力搜索最多只需要几毫秒。这意味着在C/C++中使用ASM或矢量内在函数。"
对于大多数数据而言,O(n)时间复杂度(不考虑维度灾难)对于查询(一旦树被构建)应该是摊销的O(m log n),其中m是查询集中椭圆的数量,n是数据集中椭圆的数量。构建数据本身不应该超过O(n log n)。将所有内容乘以Exp(d),其中d是维数 - 这就是这种类型的方法。