N.B:问题的底部有一个重要的编辑 - 检查出来
说我有一套要点:
我希望找到围绕它的点数最多的点,在半径(即圆圈)内或在(即正方形)的2维点内。我将它称为最密集的函数。
对于此问题中的图表,我将周围区域表示为圆圈。在上图中,中间点的周围区域以绿色显示。此中间点具有半径内所有点的最大周围点,并且将由最密集的点函数返回。
解决此问题的可行方法是使用范围搜索解决方案; this answer进一步解释,并且“ 最坏情况时间”。使用它,我可以获得每个点周围的点数,并选择具有最大周围点数的点。
然而,如果积分非常密集(大约一百万),那么:
然后,这些百万点()中的每一个都需要进行范围搜索。最差情况时间,其中 是范围内返回的点数,对于以下点树类型为真:
因此,对于组内所有点的半径内的一组点,它为每个点提供了的复杂性。这产生了超过一万亿次的操作!
有关实现这一目标的更有效,更精确的方法的任何想法,以便我能够在合理的时间内(最好或更少)找到一组点数最多的点数?
原来上面的方法是正确的!我只需要帮助实现它。
如果我使用二维范围树:
我会在每一点上执行此操作 - 产生我想要的复杂度!
但是,我无法弄清楚如何为2d分层范围树的计数查询编写代码。
我找到了一个关于范围树的great resource (from page 113 onwards),包括2d-range树的伪代码。但我无法弄清楚如何引入分数级联,也不知道如何正确实现计数查询,使其具有O(log n)
复杂度。
我还在Java中找到了两个范围树实现here和here,在C ++ here中找到了一个,尽管我不确定它是否使用了分数级联,如上所述
的countInRange
方法
在最坏的情况下返回这些点的数量 * O(log(n)^ d)时间。在最坏的情况下,它也可以返回矩形中的点 * O(log(n)^ d + k)时间,其中k是矩形中的点数。
这对我来说意味着它不适用于分数级联。
为了回答上面的问题,我需要知道的是,是否存在具有分数级联的2d范围树的库,其具有复杂度的范围计数查询,所以我不会重新发明任何轮子,或者你能帮助我编写/修改上面的资源来执行复杂性的查询吗?
如果你能提供任何其他方法来实现中任意其他方式的2d点数的范围计数查询,也不要抱怨!
答案 0 :(得分:1)
我首先要创建像https://en.wikipedia.org/wiki/K-d_tree这样的东西,在树上有一个带叶点的树,以及每个节点有关其后代的信息。在每个节点,我会保留一个后代数的计数,以及一个包围这些后代的边界框。
现在,对于每一点,我都会递归搜索树。在我访问的每个节点上,要么所有的边界框都在当前点的R内,所有的边界框都比当前点的R大,或者其中一些在R内,一些在R外。在第一个case我可以使用当前节点的后代数量的计数来增加当前点的R内的点数,并返回递归的一个级别。在第二种情况下,我可以简单地返回一级递归而不增加任何东西。只有在中间情况下我需要继续递归树。
因此,我可以为每个点计算R中邻居的数量,而不检查其他每个点,并选择具有最高计数的点。
如果点均匀分布,那么我认为你最终将构建一个kd树,其中较低的水平接近常规网格,我认为如果网格的大小为A x A,那么在最坏的情况下R足够大,以便它的边界是一个与O(A)低级单元格相交的圆,所以我认为如果你有O(n)个点你可以预期这会花费大约O(n * sqrt(n))。 / p>
答案 1 :(得分:1)
我建议使用plane sweep algorithm。这允许一维范围查询而不是二维查询。 (哪个更有效,更简单,并且在方形邻域的情况下不需要分数级联):
如果您旋转点(或只是交换X-Y坐标)以使占用区域的宽度不大于其高度,则可以优化此算法。您还可以将点切割成垂直切片(具有R大小的重叠)并分别处理切片 - 如果树中有太多元素,使其不适合CPU缓存(这不太可能只有100万个点)。该算法(优化与否)具有时间复杂度O(n log n)。
对于圆形邻域(如果R不太大且点均匀分布),您可以用几个矩形近似圆:
在这种情况下,算法的第2步应使用更多指针,以允许插入/移出多个树。在第3步,您应该在适当距离(< = R)附近的点附近进行线性搜索,以区分圆内的点与其外的点。
处理圆形邻域的其他方法是近似具有相同高度的矩形的圆(但是这里的圆应该被分成更多的部分)。这导致了更简单的算法(使用排序数组而不是订单统计树):
该算法允许前面提到的优化以及分数级联。
答案 2 :(得分:0)
您可以通过在O(n)
时间预处理数据来加速您使用的任何算法,以估算相邻点的数量。
对于半径 R 的圆,创建一个网格,其单元格在x方向和y方向都具有 R 维度。对于每个点,确定它属于哪个单元格。对于给定的单元格 c ,此测试很简单:
c.x<=p.x && p.x<=c.x+R && c.y<=p.y && p.y<=c.y+R
(您可能想深入思考封闭或半开放区间是否正确。)
如果您具有相对密集/同质的覆盖范围,则可以使用数组来存储值。如果coverage是稀疏/异构的,您可能希望使用散列映射。
现在,考虑网格上的一个点。单元内一个点的极值位置如下所示:
细胞角落的点只能是四个细胞中有点的邻居。沿着边缘的点可以是具有六个单元中的点的邻居。不在边缘上的点是具有7-9个单元格中的点的邻居。由于一个点很少精确地落在角落或边缘上,我们假设焦点单元格中的任何点都是与所有8个周围单元格中的点相邻的。
因此,如果 p 点位于单元格(x,y)中,N[p]
会识别p
内的Np[y][x]
个邻居数radius R ,N[p]
表示单元格(x,y)中的点数,然后N[p] = Np[y][x]+
Np[y][x-1]+
Np[y-1][x-1]+
Np[y-1][x]+
Np[y-1][x+1]+
Np[y][x+1]+
Np[y+1][x+1]+
Np[y+1][x]+
Np[y+1][x-1]
由下式给出:
O(n)
一旦我们估算了每个点的邻居数量,我们就可以将该数据结构堆积到best_found
时间的最大值(例如make_heap)。该结构现在是一个优先级队列,我们可以按照估计的邻居数排序每个查询的 O(log n)时间点。
对第一点执行此操作并使用 O(log n + k)圈搜索(或更聪明的算法)来确定该点的实际邻居数。在变量N[p]
中记下此点并更新其N[best_found]
值。
窥视堆顶部。如果估计的邻居数量小于parameters = (testid, username, fullname, firstnumber, secondnumber, floatnumber, posts, country, bio, link)
query = """
INSERT INTO
info
VALUES
(%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)"""
cur.execute(query, parameters)
,那么我们就完成了。否则,请重复上述操作。
要改进估算,您可以使用更精细的网格,如下所示:
以及一些巧妙的滑动窗口技术,以减少所需的处理量(例如,对于矩形情况,请参阅this answer - 对于圆形窗口,您应该使用一组FIFO队列)。为了提高安全性,您可以随机化网格的原点。
再次考虑你提出的例子:
很明显,这种启发式方法有可能节省大量时间:使用上述网格,只需要执行一次昂贵的检查,以证明中间点具有最多的邻居。同样,更高分辨率的网格将改善估算并减少需要进行的昂贵支票的数量。
你可以而且应该使用类似的边界技术与mcdowella's answers一起使用;然而,他的答案并没有提供一个开始寻找的好地方,所以可以花很多时间去探索低价值点。