从折线中删除循环的高效算法

时间:2011-11-22 09:29:15

标签: algorithm computational-geometry

我有一条折线,以一组有序的(X,Y)坐标给出,可以跨越自身形成一个或多个循环。由此,我想提取一个已删除循环的多边形,其中包括该线与自身交叉的交点。我目前有一个粗暴的蛮力算法,其工作原理如下;

int RemoveLoops(CoordType Points[],int NoPoints)
{
    int n = 0;  // Start of first line segment
    int m = 2;  // Offset to n of second line segment
    int p = NoPoints;
    while (n + m + 1 < p)
    {
        CoordType Ip; // Intersection point
        if (Intersect(Points[n],Points[n+1],Points[n+m],Points[n+m+1],Ip))) 
        {
            Points[n+1] = Ip;
            int d = m - 1;  // Number of points to delete
            for (int i = n + m + 1; i < p; i++)
                Points[i - d] = Points[i];
            p -= d;
            m = 2;
            continue;   // Restart from intersection point 
        }
        m ++;
        if (n + m + 1 >= p) // Reached end of line, change starting segment
        {
            m = 2;  // Reset offset
            n++;    // Increment starting segment
        }
    }
    return(p);  // Return the number of points in the new poly line
}

虽然我对上述内容进行了一些优化,例如通过计算连续线段之间的累积角度,我们知道在我们经过360度之前我们不能碰到一个交叉点,算法仍然是一个非常可怕的O(n ^ 2)。我知道我可以做得比这更好,例如使用set of all intersecting line segments例程作为起点。鉴于已经订购了积分,我认为我应该能做得更好。请注意,上述版本可以正常工作,并返回剩余点数,这很方便但不是必需的。

关于上述优秀算法的任何想法,O(n log n)或更好?

2 个答案:

答案 0 :(得分:0)

尝试使用sweep line algorithm方法。直觉上,最好以图形方式考虑算法。您将点放在平面中并从左到右“扫过”它们。在清扫时,您对已经发现的线条的状态保持不变。如果两条线之间存在交叉,则必须在我们已经“发现”的两条线之间发生。从技术上讲,你不必“扫描”飞机:你可以在每次偶然发现一个点时检查不变量。因此,您可以按x坐标对点进行排序,然后逐个查看。

算法的瓶颈是可以在O(nlogn)中完成的排序。我很确定你不能比nlog n做得更好,因为这些类型的问题通常可以归结为排序。您可以将此问题减少到找到一组点的凸包,这也不可能在n log n以内。

答案 1 :(得分:0)

导致加速或更好算法的通常假设是多边形链是简单的或凸的。一开始,你的连锁店都没有。

可能有一个增量数据结构可以进行O(log n + s)线简单多边形链交叉测试,但我怀疑即使它存在,它也会比仅仅进行分段交叉更复杂并且可能更慢。 / p>