我有一组经纬度坐标。如何高效地计算集合中任意两个点之间的最大距离(如果您愿意,称之为“最大直径”)?
一种朴素的方法是使用Haversine公式来计算每两个点之间的距离并获取最大值,但这显然不具有可扩展性。
编辑:这些点位于一个足够小的区域内,测量范围是人们在单日活动中携带移动设备的区域。
我有一组经纬度坐标。如何高效地计算集合中任意两个点之间的最大距离(如果您愿意,称之为“最大直径”)?
一种朴素的方法是使用Haversine公式来计算每两个点之间的距离并获取最大值,但这显然不具有可扩展性。
编辑:这些点位于一个足够小的区域内,测量范围是人们在单日活动中携带移动设备的区域。
其中C通常为0,但如果点集穿过λ=±180°线,则可以为±360°。要找到最大距离,您只需找到
(您不需要平方根,因为它是单调的)
同样的坐标变换可以用于重复步骤1(在新的坐标系统中),以获得更好的起点。我怀疑,如果满足某些条件,则上述步骤(不重复步骤3)总是导致“真实的远距离对”(我的术语)。如果我只知道哪些条件...质心由公式x(M) = Σx(P)/n给出,等等, 而要寻找的最大值是
所以:首先将球坐标转换为直角坐标,然后从质心开始,至少在两个步骤(步骤2和3)中找到距离前一个点最远的点。您可以重复步骤3,只要距离增加,可能有最大重复次数,但这不会使您远离局部最大值。如果点分布在整个地球上,则从质心开始也没有太大帮助。
编辑2:
我学会了足够的R语言来写下算法的核心(用于数据分析的好语言!)
对于平面近似,忽略λ=±180°线周围的问题:
# input: lng, lat (vectors)
rad = pi / 180;
x = (lng - mean(lng)) * cos(lat * rad)
y = (lat - mean(lat))
i = which.max((x - mean(x))^2 + (y )^2)
j = which.max((x - x[i] )^2 + (y - y[i])^2)
# output: i, j (indices)
# input: lng, lat
rad = pi / 180
x = sin(lat * rad)
f = cos(lat * rad)
y = sin(lng * rad) * f
z = cos(lng * rad) * f
i = which.max((x - mean(x))^2 + (y - mean(y))^2 + (z - mean(z))^2)
j = which.max((x - x[i] )^2 + (y - y[i] )^2 + (z - z[i] )^2)
k = which.max((x - x[j] )^2 + (y - y[j] )^2 + (z - z[j] )^2) # optional
# output: j, k (or i, j)
k
可以被省略(即结果可以由 i
和 j
给出),这取决于数据和要求。另一方面,我的实验表明,计算进一步的指数是无用的。# input: lng, lat
rad = pi / 180
x = (lng - mean(lng)) * cos(lat * rad)
y = (lat - mean(lat))
i.n_1 = 1 # n_1: n-1
x.n_1 = mean(x)
y.n_1 = 0 # = mean(y)
s.n_1 = 0 # s: square of distance
repeat {
s = (x - x.n_1)^2 + (y - y.n_1)^2
i.n = which.max(s)
x.n = x[i.n]
y.n = y[i.n]
s.n = s[i.n]
if (s.n <= s.n_1) break
i.n_1 = i.n
x.n_1 = x.n
y.n_1 = y.n
s.n_1 = s.n
}
i.m_1 = 1
x.m_1 = (x.n + x.n_1) / 2
y.m_1 = (y.n + y.n_1) / 2
s.m_1 = 0
m_ok = TRUE
repeat {
s = (x - x.m_1)^2 + (y - y.m_1)^2
i.m = which.max(s)
if (i.m == i.n || i.m == i.n_1) { m_ok = FALSE; break }
x.m = x[i.m]
y.m = y[i.m]
s.m = s[i.m]
if (s.m <= s.m_1) break
i.m_1 = i.m
x.m_1 = x.m
y.m_1 = y.m
s.m_1 = s.m
}
if (m_ok && s.m > s.n) {
i = i.m
j = i.m_1
} else {
i = i.n
j = i.n_1
}
# output: i, j
这个三维算法可以以类似的方式进行修改。在二维和三维情况下,如果找到第二对点,可以再次从中点开始重新开始。在这种情况下,上限留给读者自行思考。
修改后的算法与(过于)简单的算法进行比较,对于正常和方形均匀分布,处理时间几乎翻倍,并且平均误差从0.6%降低到0.03%(数量级)。从中点重新开始会导致稍微更好的平均误差,但最大误差几乎相等。
编辑4:
我还需要研究这篇文章,但看起来用圆规和直尺找到的20%实际上是1-1/√(5-2√3)≅ 19.3%
r
中实现这个功能?(这是原帖作者试图实现的编程语言)。 - Simon O'Hanlongeosphere::distHaversine(c(lat[i], lng[i]), c(lat[j], lng[j]))
计算。 - Jeroen Ooms这是一个天真的例子,不太适用于大规模应用(正如你所说),但可能有助于在R中构建解决方案。
## lonlat points
n <- 100
d <- cbind(runif(n, -180, 180), runif(n, -90, 90))
library(sp)
## distances on WGS84 ellipsoid
x <- spDists(d, longlat = TRUE)
## row, then column index of furthest points
ind <- c(row(x)[which.max(x)], col(x)[which.max(x)])
## maps
library(maptools)
data(wrld_simpl)
plot(as(wrld_simpl, "SpatialLines"), col = "grey")
points(d, pch = 16, cex = 0.5)
## draw the points and a line between on the page
points(d[ind, ], pch = 16)
lines(d[ind, ], lwd = 2)
## for extra credit, draw the great circle on which the furthest points lie
library(geosphere)
lines(greatCircle(d[ind[1], ], d[ind[2], ]), col = "firebrick")
如果需要更多的距离计算选项,可以使用geosphere
包。在这里使用的详细信息请参见sp中的?spDists
。
编辑#2:Barequet-Har-Peled algorithm(由Spacedman在他的回复中指出)对于e>0具有O((n+1/(e^3))log(1/e))的复杂度,值得探索。
对于准平面问题,这被称为“凸包直径”,由三部分组成:伪代码和讨论链接:http://fredfsh.com/2013/05/03/convex-hull-and-its-diameter/
请参阅此处有关相关问题的讨论:https://gis.stackexchange.com/questions/17358/how-can-i-find-the-farthest-point-from-a-set-of-existing-points
编辑:Spacedman的解决方案指向了Malandain-Boissonnat算法(请参见pdf中的论文here)。然而,这比暴力朴素O(n ^ 2)算法更糟或相同。