我有一个~30k位置的列表L(写为经度/纬度对),以及~1m事件列表E(位置写为经度/纬度对),每个位置都出现在L中的一个点上。我想用E中的相应位置标记E中的每个事件。但是L和E中的坐标是不同的舍入 - E到五个小数位,L到十三 - 所以表面上相同的坐标实际上可以相差~10 ^ -5度,或约1米。 (L中的点间隔至少约10米。)
因此,我需要L中最近的点到E的每个点;明显的O(| L || E |)暴力算法太慢了。与E相比,L足够小,预处理L并在E上分摊预处理时间的算法很好。这是一个研究得很好的问题吗?我能找到的链接是针对相关但不同的问题,比如找到一组中一对点之间的最小距离。可能相关:Voronoi diagrams,虽然我看不出如何预处理L到Voronoi图中会如何节省计算时间。
答案 0 :(得分:1)
是的,你是对的。首先,您可以使用Furtune's Sweep Line方法在O(| L | log | L |)时间内构建位置集L的Voronoi图。可以使用各种实现,Triangle将是最常见的实现。
现在你有一个O(| L |)大小的平面分区。要允许O(log | L |)最近邻查询,您需要在Voronoi图上方的搜索结构。一种常见的方法是使用Dobkin-Kirkpatrick层次结构,详细信息可以在各种lecture notes中找到。此方法支持O(log | L |)查询,并且只需要O(| L |)大小。 (在this post中也有提及。)
然后是| E |查询可以在O(| E | log | L |)时间内完成。
另一种方法是使用k-d trees。从实施的角度来看,它们可能工作量较少,并提供相同的复杂性(据我所知)。 快速搜索显示这两个可能值得测试的实现:C++,Java。
答案 1 :(得分:1)
这适用于space-partitioning的相当直接的应用。大多数GIS库都应该提供这样的工具。
我自己的经验仅限于使用R-Trees。我创建了L项的索引。您可以直接使用这些点,也可以使用表示该点周围不确定性的边界框。然后R树支持n个最近的相邻点/边界框的有效(对数(L)时间)查询。
我成功使用的实现是libspatialindex包装到名为Rtree的python库中。
但请注意,此特定工具仅在使用欧几里德x,y坐标时才准确。使用远离赤道的纬线长度存在很大的误差,特别是如果你想覆盖地理上较大的区域。在我的情况下,我被限制在一个国家,我在其中使用eastings / northings。我不知道哪个库提供了使用大圆距离处理问题的支持,但它们当然应该可用。
答案 2 :(得分:1)
根据您的描述:
解决方案: L中的圆坐标与E的精度相同,并匹配相等的对。
说明:舍入有效地将每个点映射到方形网格上的最近邻居。只要网格分辨率(舍入到五位小时约1米)低于L(~10 / 2米)中两点之间的最小距离的一半,就可以了。
为了获得最佳性能并利用| L | << | E |,您可能希望在L中的圆角坐标上构建哈希映射,并在接近恒定的时间内匹配E中的每个点。然后,总运行时间将受到O(| E |)的限制。
答案 3 :(得分:0)
我的图书馆GeographicLib包含一个班级NearestNeighbor
它实现了vantage-point trees。这适用于任何数据
哪里有真正的距离指标;明确的测地距离
满足这个标准。
基本上,您使用L
位置初始化课程
然后为每个E
事件你
询问最近的位置。关于计算的细分
费用如下
set-up cost: L log L
cost per query: log L
cost of all queries: E log L
total cost: (L + E) log L
(这些是基础2日志。)这里将执行查找的代码。在30k位置和1M事件上运行此操作大约需要40秒,并且总共需要16M测地距离计算。 (蛮力方式大约需要21个小时。)
// Read lat/lon locations from locations.txt and lat/lon events from
// events.txt. For each event print to closest.txt: the index for the
// closest location and the distance to it.
// Needs GeographicLib 1.47 or later
// compile and link with
// g++ -o closest closest.cpp -lGeographic \
// -L/usr/local/lib -Wl,-rpath=/usr/local/lib
#include <iostream>
#include <vector>
#include <fstream>
#include <GeographicLib/NearestNeighbor.hpp>
#include <GeographicLib/Geodesic.hpp>
using namespace std;
using namespace GeographicLib;
// A structure to hold a geographic coordinate.
struct pos {
double lat, lon;
pos(double lat = 0, double lon = 0)
: lat(lat), lon(lon) {}
};
// A class to compute the distance between 2 positions.
class DistanceCalculator {
private:
Geodesic _geod;
public:
explicit DistanceCalculator(const Geodesic& geod)
: _geod(geod) {}
double operator() (const pos& a, const pos& b) const {
double s12;
_geod.Inverse(a.lat, a.lon, b.lat, b.lon, s12);
return s12;
}
};
int main() {
// Define a distance function object
DistanceCalculator distance(Geodesic::WGS84());
// Read in locations
vector<pos> locs;
{
double lat, lon;
ifstream is("locations.txt");
while (is >> lat >> lon)
locs.push_back(pos(lat, lon));
}
// Construct NearestNeighbor object
NearestNeighbor<double, pos, DistanceCalculator>
locationset(locs, distance);
ifstream is("events.txt");
ofstream os("closest.txt");
double lat, lon, d;
vector<int> k;
while (is >> lat >> lon) {
pos event(lat, lon);
d = locationset.Search(locs, distance, event, k);
os << k[0] << " " << d << "\n";
}
}