问题:
在二维平面上给定N个点,同一条直线上最多有多少个点?
该问题有一个O(N2)的解决方案:遍历每个点,并找到与当前点具有相同dx / dy
关系的点数。为了提高效率,可以将dx / dy
关系存储在哈希映射中。
是否有比O(N2)更好的解决方案?
问题:
在二维平面上给定N个点,同一条直线上最多有多少个点?
该问题有一个O(N2)的解决方案:遍历每个点,并找到与当前点具有相同dx / dy
关系的点数。为了提高效率,可以将dx / dy
关系存储在哈希映射中。
是否有比O(N2)更好的解决方案?
在标准计算模型中,解决这个问题的最佳方法可能不会比O(n^2)更好。
找到三个共线点的问题归结为找到经过最多点的直线的问题,而找到三个共线点是3SUM难的,这意味着在少于O(n^2)的时间内解决它将是一个重大的理论成果。
请参见关于查找三个共线点的先前的问题。
为了方便参考(使用已知证明),假设我们想要回答一个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和重复元素y使得x + 2y = 0(也可以使用排序在O(n lg n)的时间内进行),然后在缩小到共线点查找问题之前删除重复项。
如果将问题限制在通过原点的直线上,你可以将点转换为极坐标(角度,距离原点的距离)并按角度排序。所有具有相同角度的点位于同一条直线上。O(n logn)
我认为在一般情况下没有更快的解决方案。
poix
将是一个点(x,y)对。如果线穿过x轴,则poix
将是一些数字和0。如果线平行于x轴,则poix=(无穷大,某个数字),其中y值是线穿过y轴的位置。Slope
和poix
相等时,2个对象相等。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);
使用点线对偶变换将点p=(a,b)移动到双重平面中,其中p*:y=a*x + b。 现在使用扫描线算法以NlogN时间找到所有交点。 (如果您有一个点在另一个点上方,只需将点旋转到一些小角度)。 交点对应于原始平面中的直线。
有人说,由于3SUM问题可以转化为此问题并且复杂度为O(n^2),所以该问题的复杂度也是O(n^2)。但请注意,3SUM问题的复杂度低于该问题。 请查看https://en.wikipedia.org/wiki/3SUM并阅读https://tmc.web.engr.illinois.edu/reduce3sum_sosa.pdf。
如前所述,解决这个问题的一般情况可能没有比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)
由于问题(即甚至检查R ^ 2中的3个点是否共线)是3Sum-hard(http://en.wikipedia.org/wiki/3SUM),因此不太可能存在$ o(n ^ 2)$算法。
这并不是比O(n^2)更好的解决方案,但你可以按照以下步骤进行:
2.将这组新的翻译点相对于新的(0,0)角度进行翻译。
3.保持最大数(MSN),即每个角度中存在的点数。
4.选择已存储的最大数(MSN),这将是解决方案。
n-1
个点上运用一个操作n
次。这是O(n^2)的时间复杂度。 - Matthieu M.
k*x[i] + b = y[i]
,您将得到关于k
和x
的方程。在 {k,x} 空间中,它将成为一条直线。因此,这就成为了一个通过一个点的最大直线数量的问题。它可能有解决方案。 - Vovaniumk
和b
必须是有理数,使得b == y - k*x
,其中y
和x
是整数。也许通过将问题转化为“找到满足大多数方程的这样的有理数b
和k
”的形式会有所帮助。 - ruslik