我想解决的问题如下:
假设您在二维空间中获得了一组点以及如何 我们可以得到最大数量的共线点。
我在Java中解决了这个问题。 首先,我创建了一个检查线性度的方法:
return (y1 - y2) * (x1 - x3) = (y1 - y3) * (x1 - x2);
然后我使用了三个for
循环,这使得我的算法为O(n ^ 3)。但我试图看看这是否可以减少到O(n ^ 2)。
在网上搜索后,我发现我的实现非常类似于什么here。所以问题是我们如何才能提高复杂性。任何一个例子都会很棒。
这就是我最终做的事情:
int p = 2;
for (int i = 0; i < points.lenght(); i++) {
for (int j = i+1; j < points.length(); j++) {
int count = 2;
for (int k =0; i < points.length(); k++) {
if (k == i || k == j)
continue;
//use linearity function to check if they are linear...
}
p = max(p,count);
}
}
答案 0 :(得分:4)
我找到了与@hqt解决方案非常相似的内容,并希望详细说明他们遗漏的细节。
如果它们的比率dx
与dy
之比(即斜率)相同,则此类的两个元素相等。
static class Direction {
Direction(Point p, Point q) {
// handle anti-parallel via normalization
// by making (dx, dy) lexicographically non-negative
if (p.x > q.x) {
dx = p.x - q.x;
dy = p.y - q.y;
} else if (p.x < q.x) {
dx = q.x - p.x;
dy = q.y - p.y;
} else {
dx = 0;
dy = Math.abs(p.y - q.y);
}
}
public boolean equals(Object obj) {
if (obj==this) return true;
if (!(obj instanceof Direction)) return false;
final Direction other = (Direction) obj;
return dx * other.dy == other.dx * dy; // avoid division
}
public int hashCode() {
// pretty hacky, but round-off error is no problem here
return dy==0 ? 42 : Float.floatToIntBits((float) dx / dy);
}
private final int dx, dy;
}
现在通过遍历所有对(复杂Multimap<Direction, PointPair>
)来填充番石榴O(n*n)
。迭代所有键(即方向)并通过union find algorithm处理List<PointPair>
。找到的分区是一对共线点。如果有k
个共线点,那么您将找到一个包含所有对的点。
由于联合查找算法,复杂度为O(n*n*log(n))
,避免排序没有帮助。
答案 1 :(得分:3)
你可以使用Ox之间的两点之间的角度系数来解决这个问题。例如,对于3个点:A B C.当且仅当线AB和线AC与Ox线具有相同的角系数时它们是共线的。所以,这是我的伪代码:
// Type : an object to store information to use later
List<Type> res = new ArrayList<Type>();
for (int i = 0; i < points.lenght(); i++) {
for (int j = i+1; j < points.length(); j++) {
double coefficient = CoeffiecientBetweenTwoLine(
line(points[i], points[j]), line((0,0), (0,1));
res.add(new Type(points[i], points[j], coefficient);
}
}
之后,您使用QuickSort,再次按照Coefficient在List基础上进行排序。任何系数都等于,我们可以知道哪些点是共线的。此算法的复杂性为O(N^2logN)
(通过使用O(N^2)
元素对列表进行排序,仅构建列表所需的O(N^2)
。
<强> @Edit:强> 那么当我们显示相等的系数时,我们怎么能知道有多少个共线? 有很多方法可以解决这个问题。
在排序步骤中,您可以按第一个参数排序(是哪个点 那条线)当两个系数相等时。例如。排序后, 结果应该是(在这种情况下,如果1 3和4是共线的):
(1 3) (1 4) (3 4)
从上面的建筑物中,你只需要看到1的条纹,在这个例子中,是2.所以结果应该是3.(总是k + 1)
n*(n-1)/2
。所以,你将拥有:n*(n-1)/2 = 3
。你可以知道n = 3(n> =
0)。这意味着你可以在这里解决二次方程(但不是
很难,因为你总是知道它有解决方案,而且只是得到
一个积极的解决方案 编辑2
知道有多少共线点的上述步骤不正确,因为例如,A B和C D是两条平行线(并且线AB与线CD不同),结果,它们仍然具有与Ox相同的系数。所以,我认为要解决这个问题,你可以使用Union-Find
数据结构来解决这个问题。步骤将是:
再次对角系数进行排序 例如:(1 2 3 4)是共线的,它们与(5,6,7)平行,而点8在其他地方。因此,排序后,结果应为:
(1 2)(1 3)(1 4)(2 3)(2 4)(5 6)(5,7)(6,7)角系数等于,但是在两条不同的线上
(1,5)(1,6).. //在两组平行线之间会有一对连接。 (1,8)
(5,8)(3,8).... //随机顺序。因为不知道。
使用Union-Find Data结构连接树:从第二个元素开始迭代,如果您看到它的角度系数等于之前,则加入自身并加入之前的元素。例如,
(1,3)==(1,2):加入1和2,加入1和3.
(1,4)==(1,3):加入1和3,加入1和4. ....
(5,6):加入2和4,加入5和6.
(5,7):加入5和7,加入5和6 ...
(1,8):不加入任何东西。 (5,8):不加入任何东西......
完成此步骤后。你所拥有的只是一棵多树,在每棵树中,是一组它们共线的点。
在上面的步骤中,您会看到一些对多次加入。您可以通过标记简单地修复它,如果它们已经加入,则忽略以提高性能。
@:我认为这个解决方案并不好,我只是通过脑力思考,而不是真正的算法背后。所以,任何其他明确的想法,请告诉我。
答案 2 :(得分:2)
尝试下面
//just to create random 15 points
Random random = new Random();
ArrayList<Point> points = new ArrayList<Point>();
for(int i = 0; i < 15; i++){
Point p = new Point(random.nextInt(3), random.nextInt(3));
System.out.println("added x = " + p.x + " y = " + p.y);
points.add(p);
}
//code to count max colinear points
int p = 0;
for(int i = 0; i < points.size() -1; i++){
int colinear_with_x = 1;
int colinear_with_y = 1;
for(int j = i + 1; j < points.size(); j++){
if(points.get(i).x == points.get(j).x){
colinear_with_x++;
}
if(points.get(i).y == points.get(j).y){
colinear_with_y++;
}
}
p = max(p,colinear_with_x,colinear_with_y);
}
答案 3 :(得分:2)
一种严重依赖于良好散列映射的方法:
关键是使用线性方程(定义一条直线),这样就可以得到一条沿
线的地图map<key=(vector, point), value=quantity> pointsOnLine
其中向量和点定义两点确定的线性函数。
然后你迭代所有n个点:
maxPoints = 2
for i=1 to n
for j=i+1 to n
newKey = lineParametersFromPoints(point[i], point[j])
if pointsOnLine.contains(newKey)
pointsOnLine[key] += 1
if maxPoints < pointsOnLine[key]
maxPoints = pointsOnLine[key]
else
pointsOnLine.add(key)
pointsOnLine[key] = 2
maxPoints
则包含最大共线点数。
请注意(这可能是最重要的),地图的哈希比较函数必须检查两行表示相同的线性函数,即使向量是反平行的或两条线上的点不一样(但是一条线满足另一条方程)。
这种方法当然严重依赖于具有快速访问和插入时间的地图。
答案 4 :(得分:1)
算法: -
可以在O(N^2*logN)
中执行的简单算法: -
- 选择一个点p。
- 找到从p到所有其他点的斜率
- 根据斜率对点进行排序
- 扫描已排序的数组以检查具有相同斜率值的最大连续点
- 最大值+ 1是与p包含的最大点。
- 对所有点执行1到5并找到找到的最大共线点
醇>
时间复杂度: O(N)
用于斜率计算,O(NlogN)
用于排序,O(N^2*logN)
用于所有N点。
空间复杂度 O(N)