找到贯穿大多数点的直线的最有效算法是什么?

时间:2010-11-14 20:40:04

标签: algorithm geometry computational-geometry

问题:

在2维平面上给出N个点。同一行的最大点数是多少?

问题有O(N 2 )解决方案:遍历每个点并找到与当前点有相同dx / dy的点数。将dx / dy关系存储在哈希映射中以提高效率。

这个问题比O(N 2 )有更好的解决方案吗?

9 个答案:

答案 0 :(得分:38)

在标准的计算模型中,这个问题可能没有明显优于O(n ^ 2)的解决方案。

找到三个共线点的问题减少了找到通过最多点的线的问题,找到三个共线点是3SUM-hard,这意味着在小于O(n ^ 2)时间内求解它是一个重要的理论结果。

有关找到三个共线点的信息,请参阅previous question

供您参考(使用已知证明),假设我们想要回答3SUM问题,例如在列表X中查找x,y,z,使得x + y + z = 0.如果我们有一个快速算法共线点问题,我们可以使用该算法来解决3SUM问题如下。

对于X中的每个x,创建点(x,x ^ 3)(现在我们假设X的元素是不同的)。接下来,检查创建的点中是否存在三个共线点。

要看到这是有效的,请注意,如果x + y + z = 0,则从x到y的线的斜率为

(y ^ 3 - x ^ 3)/(y - x)= y ^ 2 + yx + x ^ 2

并且从x到z的线的斜率是

(z ^ 3 - x ^ 3)/(z - x)= z ^ 2 + zx + x ^ 2 =( - (x + y))^ 2 - (x + y)x + x ^ 2 = x ^ 2 + 2xy + y ^ 2 - x ^ 2 - xy + x ^ 2 = y ^ 2 + yx + x ^ 2

相反,如果从x到y的斜率等于从x到z的斜率,那么

y ^ 2 + yx + x ^ 2 = z ^ 2 + zx + x ^ 2,

暗示

(y-z)(x + y + z)= 0,

所以要么y = z或z = -x-y就足以证明减少是有效的。

如果X中有重复项,则首先检查x + 2y = 0是否为任何x和重复元素y(使用散列的线性时间或使用排序的O(n lg n)时间),然后删除重复项减少到共线寻点问题。

答案 1 :(得分:4)

如果将问题限制为通过原点的线,则可以将点转换为极坐标(角度,距原点的距离)并按角度进行排序。具有相同角度的所有点位于同一直线上。 O(n logn)

在一般情况下,我认为没有更快的解决方案。

答案 2 :(得分:4)

Hough Transform可以为您提供近似解决方案。它是近似的,因为分箱技术在参数空间中具有有限的分辨率,因此最大分档将为您提供一些有限的可能线路范围。

答案 3 :(得分:1)

使用 p=(a,b) p*:y=a*x + b 的点线对偶变换移动到对偶平面。 现在使用线扫描算法在 NlogN 时间内找到所有交点。 (如果您有一个位于另一个上方的点,只需将这些点旋转到某个小角度即可)。 双平面中的交点对应于引物平面中的线。

答案 4 :(得分:1)

谁说因为 3SUM 减少了这个问题,因此复杂度是 O(n^2)。请注意,3SUM 的复杂度低于此。 请检查 https://en.wikipedia.org/wiki/3SUM 并阅读 https://tmc.web.engr.illinois.edu/reduce3sum_sosa.pdf

答案 5 :(得分:0)

再次使用伪代码的O(n ^ 2)解决方案。 Idea是以行本身为键创建哈希表。线由两点之间的斜率定义,线切割x轴的点和线切割y轴的点。

解决方案假定Java,C#等语言将equals方法和对象的hashcode方法用于散列函数。

创建一个包含3个字段的对象(调用SlopeObject)

  1. 斜率//可以是无限
  2. x轴的截距点 - poix //将是(无穷大,某些y值)或(x值,0)
  3. 计数
  4. poix将是一个点(x,y)对。如果线穿过x轴,poix将(某个数字,0)。如果线与x轴平行,则poix =(无穷大,某个数字),其中y值是线与y轴交叉的位置。 如果Slopepoix相等,则覆盖等于2个对象相等的方法。

    Hashcode被一个函数覆盖,该函数根据Slopepoix的值的组合提供哈希码。

    下面的一些伪代码
    Hashmap map;
    foreach(point in the array a) {
        foeach(every other point b) {
            slope = calculateSlope(a, b);
            poix = calculateXInterception(a, b);
            SlopeObject so = new SlopeObject(slope, poix, 1); // Slope, poix and intial count 1.
            SlopeObject inMapSlopeObj = map.get(so);
            if(inMapSlopeObj == null) {
                inMapSlopeObj.put(so);
            } else {
                inMapSlopeObj.setCount(inMapSlopeObj.getCount() + 1);
            }
        }
    }
    SlopeObject maxCounted = getObjectWithMaxCount(map);
    print("line is through " + maxCounted.poix + " with slope " + maxCounted.slope);
    

答案 6 :(得分:0)

如前所述,可能没有比O(n ^ 2)更好地解决这个问题的一般情况的方法。但是,如果假设大量的点位于同一条线上(比如点集中的随机点位于具有最大点数的线上的概率为p)并且不需要精确的点算法,随机算法更有效。

maxPoints = 0
Repeat for k iterations:
    1. Pick 2 random, distinct points uniformly at random
    2. maxPoints = max(maxPoints, number of points that lies on the 
       line defined by the 2 points chosen in step 1)

请注意,在第一步中,如果您选择了具有最大点数的线上的2个点,您将获得最佳解决方案。假设n非常大(即我们可以将找到2个理想点的概率视为替换的采样),这种情况发生的概率是p ^ 2。因此,在k次迭代之后找到次优解的概率是(1-p ^ 2)^ k。

假设您可以容忍错误的负利率=错误。然后该算法在O(nk)= O(n * log(err)/ log(1-p ^ 2))中运行。如果n和p都足够大,则这比O(n ^ 2)明显更有效。 (即假设n = 1,000,000并且你知道至少有10,000个点位于同一条线上。然后n ^ 2将需要10 ^ 12次操作的幅度,而随机算法需要10 ^ 9次操作的幅度获得小于5 * 10 ^ -5的错误率。)

答案 7 :(得分:-1)

$ o(n ^ 2)$算法不太可能存在,因为问题(甚至检查R ^ 2中的3个点是否共线)是3Sum-hard(http://en.wikipedia.org/wiki/3SUM

答案 8 :(得分:-3)

这不是比O(n ^ 2)更好的解决方案,但您可以执行以下操作,

  1. 对于每个点转换,首先将其转换为在(0,0)坐标中的位置,然后通过移动它们移动原始选择所需的相同x,y距离对所有其他点执行等效转换点。
  2. 2.将这组新的平移点转换为相对于新(0,0)的角度。

    3.Keep存储每个角度的点的最大数量(MSN)。

    4.选择最大存储号码(MSN),这将是解决方案