【问题标题】:Reduce time taken to find N line intersection减少寻找 N 线交点的时间
【发布时间】:2016-11-12 19:53:05
【问题描述】:

N 个线段,水平或垂直。现在我需要找出每条线段的交点总数和交点总数。 N 可以达到 100000。我试着检查每一对线。答案是正确的,但我需要减少它所花费的时间。

这是我的代码:

using namespace std;

typedef struct Point
{
     long long int x;
     long long int y;
} ;


bool fun(Point p0, Point p1, Point p2, Point p3)
{
    double s1_x, s1_y, s2_x, s2_y;
    s1_x = p1.x - p0.x;     s1_y = p1.y - p0.y;
    s2_x = p3.x - p2.x;     s2_y = p3.y - p2.y;

    double s, t;
    s = (-s1_y * (p0.x - p2.x) + s1_x * (p0.y - p2.y)) / (-s2_x * s1_y + s1_x * s2_y);
    t = ( s2_x * (p0.y - p2.y) - s2_y * (p0.x - p2.x)) / (-s2_x * s1_y + s1_x * s2_y);

    if (s >= 0 && s <= 1 && t >= 0 && t <= 1)
    {
        return 1; // Collision detected
    }

    return 0; // No collision
}


int main()
{
     long long int n // number of line segments;

     Point p[n],q[n]; // to store end points of line segments

    for( long long int i=0;i<n;i++)
    {

// line segments is defined by 2 points P(x1,y1) and Q(x2,y2)
        p[i].x=x1;
        p[i].y=y1;
        q[i].x=x2;
        q[i].y=y2;
    }

    for( long long int i=0;i<n-1;i++)
    {
        for( long long int j=i+1;j<n;j++)
        {
            if(fun(p[i],q[i],p[j],q[j]))
               count++;
        }

    }

    return 0;
}

有人可以帮我降低这个程序的时间复杂度吗?

【问题讨论】:

  • 如果它们是水平的或垂直的,那么检查交叉点比检查任意线容易得多(如您的方法)。此外,首先按 x/y 坐标对线条进行排序。然后你知道可能的交叉点必须在哪个区域。更复杂的方法是使用分段树或区间树。
  • 同一个老师还是同一个比赛? stackoverflow.com/questions/40570886/…
  • @MBoI 不认识他,但问题似乎相似,但我们的方法非常不同。
  • @Spektre 感谢您的帮助。

标签: c++ algorithm time-complexity line-intersection


【解决方案1】:

这是一个 O(n log n) 时间的算法,它使用带有 Fenwick 树的扫描线。

第 0 步:坐标重映射

对 x 坐标进行排序,并将每个值替换为 0..n-1 中的整数,以保持顺序。对 y 坐标执行相同的操作。相交属性被保留,同时允许下面的算法更容易实现。

第一步:平行线段

我将针对水平线段描述此步骤。对垂直段重复。

按 y 坐标对水平线段进行分组。一次处理一组,为扫描线创建事件,如下所示。每个段在其较小的端点处获得一个开始事件,在其较大的端点处获得一个停止事件。如果您想要闭合线段,则排序在停止之前开始。按排序顺序扫描事件,跟踪当前与扫描线相交的线段数和处理的开始事件数。线段的平行交叉点数为(在开始时间相交的线段数 + 在停止时间处理的开始事件数 - 在开始时间处理的开始事件数)。 (另请参阅Given a set of intervals, find the interval which has the maximum number of intersections,了解我之前对此的解释。)

第 2 步:垂直线段

我将通过计算每个水平线段与多少垂直线段相交来描述此步骤。

我们做另一个扫描线算法。事件是水平线段开始、垂直线段和水平线段停止,假设闭合线段按该顺序排序。对于每个 y 坐标,我们使用 Fenwick 树来跟踪到目前为止有多少垂直线段覆盖了该 y 坐标。要处理水平起点,请从其交点计数中减去其 y 坐标的树值。要处理水平停靠点,请将其 y 坐标的树值添加到其交点计数中。这意味着计数会增加差异,即在水平段处于活动状态时刺穿水平段的垂直段数。要处理垂直线段,请使用 Fenwick 树的强大功能快速递增其较小 y 坐标和较大 y 坐标之间的所有值(包括假设闭合线段)。

如果您愿意,可以组合这些扫描线算法。出于说明原因,我将它们分开。

【讨论】:

    猜你喜欢
    • 2015-02-28
    • 1970-01-01
    • 2014-04-09
    • 1970-01-01
    • 2012-05-26
    • 1970-01-01
    • 2011-11-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多