查找一组点中是否有4个共线点

时间:2013-12-16 07:49:36

标签: math geometry 2d

这是一个编程竞赛问题。我只是在寻求建议,因为这只是为了练习而不是为了作弊等目的。

问题是我有一组数千个点,它们有一个x和y坐标(在10 ^ -10到10 ^ 10之间)。我必须发现,如果存在任何共线的4个点,即位于直线上。

到目前为止,我所想到的是以下内容:

每对点,将y2-y1 / x2-x1存储在地图中,其中斜率为关键点,点列表为值。 存储后,查看地图中是否已存在相同的值,以及这对点是否与您当前处理的点不同。如果它们不同,那么你就有了4个共线点。

但是,此问题的空间和时间复杂度为O(N ^ 2)。而且,这涉及大量的小数和除法运算,这降低了准确性。

有人能为这个问题提出更优雅的解决方案吗?

谢谢, 乌代

3 个答案:

答案 0 :(得分:2)

您可以使用Hough transform执行此操作。

基本上,对于每个点( x y ),您可以考虑通过这一点的每条可能的线。

每一行都可以用两个参数来描述:

  • 一个参数选择是 m (线的斜率)和 b (与y轴的交点): y = < EM> MX + b'/ em>的;
  • 另一种选择是ρ(线与原点的符号距离)和θ(线的角度):ρ= x cosθ+ y sin θ

我将在下面使用ρ和θ。

通过将ρ和θ四舍五入到给定的精度,可以确保每个点都有有限数量的可能线。如果以10 8 的步长测量ρ,则ρ只有282个可能值(因为最大 x y 坐标为10 < SUP> 10 )。如果以整度测量θ,则θ只有180个可能的值。

因此,您可以在由(ρ,θ)索引的二维数组中表示每条可能的线。这称为累加器数组。让我们存储属于该数组中给定行的点数:

int[,] accumulator = new int[numRho, numTheta]
for each point (x,y):
    for θ from 0° to 179°:
        ρ = x cos θ + y sin θ
        ρ = ρ / 10^8
        accumulator[ρ, θ] ++

现在我们只需要检查累加器值为4或更多的桶,这意味着其中一条线上可能有四个或更多点落入该桶。当然,由于我们舍入ρ和θ,我们需要检查这些点是否真的位于同一条线上(这里我们可以使用天真的O( n 2 )算法,或者如果找到的点数很大,我们可以用更精细的ρ和θ细分重复这个算法。

for each possible ρ
    for each possible θ
        if accumulator[ρ, θ] >= 4
             find all points in that bucket that are close to the line (ρ, θ)
             check those points for collinearity

如果我们选择ρ和θ的分辨率足够大,大多数桶包含的四个点少,那么我们应该有一个摊销的复杂性:

  • 时间:O((点数+ρ值的数量)×θ值的数量)
  • 空间:O(ρ的值的数量×θ的值的数量)

否则我们需要一个更大的累加器数组。不过,我认为这应该比测试每一对点要快得多。

答案 1 :(得分:0)

对于每个点,Pi具有一组矢量V,表示从Pi到彼此的点Qj的连接,其中i <1。记者: 如果集合V中存在三个共线矢量,则点是共线的。 (共线测试是一个简单的vector_product)。

最差情况运行时间复杂度:O(n(n + 1)(n + 2)/ 6)== O(n ^ 3) 空间复杂度:O(1)

// Having a class Point, Vector and a function vector_product

typedef std::vector<Point> Points;
bool find_four_collinear_points(const Points& points) {
    for(unsigned i0 = 0; i0 < points.size(); ++i0) {
        const Point& p0 = points[i0];
        for(unsigned i1 = i0 + 1; i1 < points.size(); ++i1) {
            const Point& p1 = points[i1];
            if(p0 == p1) continue;
            unsigned collinear = 0;
            for(unsigned i2 = i1 + 1; i2 < points.size(); ++i2) {
                const Point& p2 = points[i2];
                if(p0 == p2) continue;
                if(p1 == p2) continue;
                if(vector_product(Vector(p0, p1), Vector(p1, p2)) == 0) {
                    if(++collinear == 3)
                        return true;
                }
            }
        }
    }
    return false;
}

答案 2 :(得分:-1)

我发现这个问题的解决方案在O(n ^ 2)中运行,可能是最简单的解决方案。您可以使它更有效,但我认为大多数迭代解决方案都是这种形式。 (它在python中)

def slope(p1,p2):
    if int(p2[0]-p1[0]) is 0:
        return False
    m = (p2[1]-p1[1])/(p2[0]-p1[0])
    return m

def fourOnALine(listoftuples):
    n = len(listoftuples)
    for i in range(n):
        slope_dict = {}
        count = 0
        for j in range(i+1,n):
            m = slope(listoftuples[i], listoftuples[j])
            if m in slope_dict:
                 slope_dict[m]+= 1
            else:
                slope_dict[m] = 1
        for key in slope_dict:
            if slope_dict[key] == 3:
                return True
    return False

print fourOnALine([(1,1),(2,2),(3,3),(8,8)])#true
print fourOnALine([(5.0,1.0),(5.0,8.0),(3.0,4.0),(7.0,12.0),(-3.0,6.0),(1.0,0.0)])#true
print fourOnALine([(3,9),(-4,9),(0,2),(1,-3),(5,4)])#false
print fourOnALine([(-1000.0,1000.0),(-2000.0,1000.0),(-3000.0,999.0),(-4000.0,1000.0)])#false