用于在有限集中找到最近点到另一点的有效算法

时间:2017-04-12 14:42:45

标签: algorithm geometry computational-geometry

我有一个~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图中会如何节省计算时间。

4 个答案:

答案 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)

根据您的描述:

  • E中的每个点都与L中的点相同,四舍五入到小数点后五位。因此,E中的每个点偏离L的匹配距离约1米。
  • L中的点间隔至少约10米。

解决方案: 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";
  }
}