-
05-07-2019 - |
题
给定平面上的一组点,找到由这些点中的任意两个点形成的最短线段。
我怎样才能做到这一点?最简单的方法显然是计算每个距离,但我需要另一种算法来比较。
解决方案
http://en.wikipedia.org/wiki/Closest_pair_of_points
该问题可以使用递归分治方法在 O(n log n) 时间内解决,例如如下:
- 沿 x 坐标对点进行排序
- 通过垂直线 x = xmid 将点集分成两个大小相等的子集
- 在左右子集中递归地解决问题。这将分别给出左侧和右侧最小距离 dLmin 和 dRmin。
- 求一对点之间的最小距离 dLRmin,其中一个点位于垂直分割线的左侧,第二个点位于垂直线的右侧。
- 最终的答案是 dLmin、dRmin 和 dLRmin 中的最小值。
其他提示
我不能立即想到比蛮力技术更快的替代方法(虽然必须有很多)但是你选择的算法不计算每个点之间的距离。如果您需要比较距离,只需比较距离的正方形,以避免昂贵且完全冗余的平方根。
一种可能性是按X坐标对点进行排序(或者Y - 并不重要,只是一致)。然后,您可以使用它来消除与许多其他点的比较。当你看到点[i]和点[j]之间的距离时,如果单独的X距离大于你当前的最短距离,那么点[j + 1] ...点[N]可以被消除为好吧(假设i<j
- 如果j<i
,那么点[0] ......点[i]被淘汰了。)
如果您的点以极坐标开始,您可以使用相同的变体 - 按原点的距离排序,如果与原点的距离差异大于当前的最短距离,则可以消除那一点,以及与您目前正在考虑的原点相比距原点更近(或更接近原点)的所有其他原因。
您可以从 Delaunay三角测量中提取最接近的线性时间对,并从< a href =“http://en.wikipedia.org/wiki/Voronoi_diagram”rel =“nofollow noreferrer”> Voronoi图。
此问题有一个标准算法,您可以在这里找到它: http://www.cs.mcgill.ca/~cs251/ClosestPair/ ClosestPairPS.html
这是我对这个算法的实现,抱歉没有评论:
static long distSq(Point a, Point b) {
return ((long) (a.x - b.x) * (long) (a.x - b.x) + (long) (a.y - b.y) * (long) (a.y - b.y));
}
static long ccw(Point p1, Point p2, Point p3) {
return (long) (p2.x - p1.x) * (long) (p3.y - p1.y) - (long) (p2.y - p1.y) * (long) (p3.x - p1.x);
}
static List<Point> convexHull(List<Point> P) {
if (P.size() < 3) {
//WTF
return null;
}
int k = 0;
for (int i = 0; i < P.size(); i++) {
if (P.get(i).y < P.get(k).y || (P.get(i).y == P.get(k).y && P.get(i).x < P.get(k).x)) {
k = i;
}
}
Collections.swap(P, k, P.size() - 1);
final Point o = P.get(P.size() - 1);
P.remove(P.size() - 1);
Collections.sort(P, new Comparator() {
public int compare(Object o1, Object o2) {
Point a = (Point) o1;
Point b = (Point) o2;
long t1 = (long) (a.y - o.y) * (long) (b.x - o.x) - (long) (a.x - o.x) * (long) (b.y - o.y);
if (t1 == 0) {
long tt = distSq(o, a);
tt -= distSq(o, b);
if (tt > 0) {
return 1;
} else if (tt < 0) {
return -1;
}
return 0;
}
if (t1 < 0) {
return -1;
}
return 1;
}
});
List<Point> hull = new ArrayList<Point>();
hull.add(o);
hull.add(P.get(0));
for (int i = 1; i < P.size(); i++) {
while (hull.size() >= 2 &&
ccw(hull.get(hull.size() - 2), hull.get(hull.size() - 1), P.get(i)) <= 0) {
hull.remove(hull.size() - 1);
}
hull.add(P.get(i));
}
return hull;
}
static long nearestPoints(List<Point> P, int l, int r) {
if (r - l == P.size()) {
Collections.sort(P, new Comparator() {
public int compare(Object o1, Object o2) {
int t = ((Point) o1).x - ((Point) o2).x;
if (t == 0) {
return ((Point) o1).y - ((Point) o2).y;
}
return t;
}
});
}
if (r - l <= 100) {
long ret = distSq(P.get(l), P.get(l + 1));
for (int i = l; i < r; i++) {
for (int j = i + 1; j < r; j++) {
ret = Math.min(ret, distSq(P.get(i), P.get(j)));
}
}
return ret;
}
int c = (l + r) / 2;
long lD = nearestPoints(P, l, c);
long lR = nearestPoints(P, c + 1, r);
long ret = Math.min(lD, lR);
Set<Point> set = new TreeSet<Point>(new Comparator<Point>() {
public int compare(Point o1, Point o2) {
int t = o1.y - o2.y;
if (t == 0) {
return o1.x - o2.x;
}
return t;
}
});
for (int i = l; i < r; i++) {
set.add(P.get(i));
}
int x = P.get(c).x;
double theta = Math.sqrt(ret);
Point[] Q = set.toArray(new Point[0]);
Point[] T = new Point[Q.length];
int pos = 0;
for (int i = 0; i < Q.length; i++) {
if (Q[i].x - x + 1 > theta) {
continue;
}
T[pos++] = Q[i];
}
for (int i = 0; i < pos; i++) {
for (int j = 1; j < 7 && i + j < pos; j++) {
ret = Math.min(ret, distSq(T[i], T[j + i]));
}
}
return ret;
}
根据您的问题,目前尚不清楚您是在寻找细分的距离还是细分本身。假设您正在寻找距离(一段简单的修改,一旦您知道哪个是距离最小的两个点),给定5个点,编号从1到5,您需要
compare 1 with 2,3,4,5, then
compare 2, with 3,4,5, then
compare 3 with 4,5, then
compare 4 with 5.
如果我没有错,考虑到距离的可交换性,你不需要进行其他比较。 在python中,可能听起来像是
import numpy as np
def find_min_distance_of_a_cloud(cloud):
"""
Given a cloud of points in the n-dim space, provides the minimal distance.
:param cloud: list of nX1-d vectors, as ndarray.
:return:
"""
dist_min = None
for i, p_i in enumerate(cloud[:-1]):
new_dist_min = np.min([np.linalg.norm(p_i - p_j) for p_j in cloud[(i + 1):]])
if dist_min is None or dist_min > new_dist_min:
dist_min = new_dist_min
return dist_min
可以使用以下代码测试:
from nose.tools import assert_equal
def test_find_min_distance_of_a_cloud_1pt():
cloud = [np.array((1, 1, 1)), np.array((0, 0, 0))]
min_out = find_min_distance_of_a_cloud(cloud)
assert_equal(min_out, np.sqrt(3))
def test_find_min_distance_of_a_cloud_5pt():
cloud = [np.array((0, 0, 0)),
np.array((1, 1, 0)),
np.array((2, 1, 4)),
np.array((3, 4, 4)),
np.array((5, 3, 4))]
min_out = find_min_distance_of_a_cloud(cloud)
assert_equal(min_out, np.sqrt(2))
如果两个以上的点可以具有相同的最小距离,并且您正在寻找段,则需要再次修改建议的代码,输出将是距离最小的点列表(或几个点) )。希望它有所帮助!