是否有可能有效地计算与数字线上的单个点P重叠的线段数?

时间:2013-06-22 19:34:59

标签: c# c++ game-physics spatial spatial-index

是否可以有效地计算与数字线上的单个点P重叠的线段数?

所有线段都位于一个数字线(1-D世界,而不是3-D世界。

每个线段都有一个起始坐标X1和一个结束坐标X2

示例:

Line segment A spans from X1==1 to X2==3
Line segment B spans from X1==2 to X2==4
Line segment C spans from X1==3 to X2==5
Line segment D spans from X1==1 to X2==4
----------------------------------------
Ex1: Line segments that overlap point P==2: A,B and D   >>> overlap count==3.
Ex2: Line segments that overlap point P==7: None        >>> overlap count==0.
Ex3: Line segments that overlap point P==3: A,B,C and D >>> overlap count==4.

当然,如果只有4个线段,那么代码很简单。但是,如果有一个包含4亿个细分市场的巨大空间数据库,那么搜索速度非常慢。

是否有任何算法可以有效地搜索这些线段列表中的重叠总数?

到目前为止我所看到的内容

  • 关于空间索引搜索算法的文章。
  • Interval trees(看起来非常有前途)。
  • Segment trees(看起来非常有前途)。
  • 的rtrees。

4 个答案:

答案 0 :(得分:3)

如果按照起始值对列表进行排序,然后再按长度排序(对于相同的起始值),则最终得到有效算法的根。

sort the list by starting value
for the same starting value, sort by length (longest first)

然后,当您需要与给定点P重叠的线段数时:

for a given value p
find the points in the list with starting value <= p (binary search - fast)
for each starting value, start with the longest length
if it spans the point of interest, increment counter
if not, go to the next smaller start value
keep going until you have reached the smallest starting value

它并不完美,但比搜索10M点要好得多(虽然最初的排序显然需要一些时间。但是你只需要做一次)。

答案 1 :(得分:1)

在单个数组中逐渐对查询点和间隔端点进行排序;对于每个点,保持一个标志,告诉它是否是间隔开始,间隔结束或查询。

将计数器初始化为零并扫描列表。开始增加计数器;结束会减少它;查询通过读取计数器来了解重叠间隔的数量。

时间(N + M)。如果可以使用特殊排序,则为(N + M)或更好。

如果不允许对查询点进行排序,只需对间隔端点进行排序。在单线性扫描中,您可以计算每个端点之后的重叠次数。

对于给定的查询点,您可以通过二分法搜索找到相关的终点,因此重叠计数。

M.Log(M)+ N.Log(M)表示M个区间和N个查询点。

如果不允许对间隔进行排序,只需对查询点进行排序。

依次处理每个区间,通过二分法搜索找到它重叠的第一个查询点,并增加它重叠的所有查询点的计数器。

N.Log(N)+ M.Log(N)+ O其中O是间隔/查询重叠的总数。

如果您根本不允许排序,请针对每个区间N.M。详细测试每个查询。

答案 2 :(得分:0)

查看间隔树或分段树以帮助解决此类问题。 This answer有一些关于这些技术如何帮助你的好例子。

答案 3 :(得分:-1)

首先要意识到你不能做得比O(N)好,因为你需要至少看一次每个线段。(其中N =线段数)

让我们有一个数组Line_segments_at,它存储通过每个点的线段数。

首先我们需要将此数组初始化为0。 然后,当我们查看第i个线段时,我们需要做:

for(int j=x1[i];j<=x2[i];j++)
 Line_segments_at[j]++;

对于每个查询点k,我们只需将结果返回为Line_segments_at [k]。