问题:
在2维平面上给出N个点。同一直行的最大点数是多少?
问题有O(N 2 )解决方案:遍历每个点并找到与当前点有相同dx / dy
的点数。将dx / dy
关系存储在哈希映射中以提高效率。
这个问题比O(N 2 )有更好的解决方案吗?
答案 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)
poix
//将是(无穷大,某些y值)或(x值,0) poix
将是一个点(x,y)对。如果线穿过x轴,poix
将(某个数字,0)。如果线与x轴平行,则poix =(无穷大,某个数字),其中y值是线与y轴交叉的位置。
如果Slope
和poix
相等,则覆盖等于2个对象相等的方法。
Hashcode被一个函数覆盖,该函数根据Slope
和poix
的值的组合提供哈希码。
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)更好的解决方案,但您可以执行以下操作,
2.将这组新的平移点转换为相对于新(0,0)的角度。
3.Keep存储每个角度的点的最大数量(MSN)。
4.选择最大存储号码(MSN),这将是解决方案