【问题标题】:Grouping set of points to nearest pairs将一组点分组到最近的对
【发布时间】:2015-08-09 22:08:03
【问题描述】:

我需要一个算法来解决以下问题:

给定平面上的一组二维点 P = { (x_1, y_1), (x_2, y_2), ..., (x_n, y_n) }。我需要按以下方式将它们成对分组:

  1. 在 P 中找到两个最近的点 (x_a, y_a) 和 (x_b, y_b)。
  2. 将 对添加到结果集 R。
  3. 从 P 中删除 。
  4. 如果初始集合 P 不为空,则转到第一步。
  5. 返回对 R 的集合。

那个简单的算法是 O(n^3),使用更快的算法来搜索最近的邻居,它可以改进到 O(n^2 logn)。能不能做得更好?

如果这些点不在欧几里得空间中呢?

一个例子(结果组用红色圆圈圈起来):

【问题讨论】:

  • 您能否详细说明一下输出究竟应该是什么,即您能否提供一个输入输出案例示例?
  • PriorityQueue<Distance> ?
  • @sircodesalot 你能详细说明一下吗?
  • 其实还是N^2 log N,但是基本上可以将所有距离dump到一个优先队列中,然后读出最近的对。
  • 使用Set<Point> 跟踪看到的项目。 Set<T> 的查找时间为 O(1)。或者你可以只使用一个位集或一个数组或其他东西(因为你知道你正在使用多少个点),这不需要摊销。

标签: algorithm computational-geometry nearest-neighbor


【解决方案1】:

你可以找到与this divide and conquer algorithm 最接近的对,它在 O(nlogn) 时间内运行,你可以重复这个 n 次,你会得到 O(n^2 logn),这并不比你得到的更好。

不过,您可以利用分治算法的递归结构。考虑一下,如果您删除的这对点位于分区的右侧,那么左侧的所有行为都会相同,那里没有任何变化,因此您只需自下而上重做 O(logn) 合并步骤.但是考虑到第一个新的合并步骤将合并 2 个元素,第二个合并 4 个元素,然后是 8,然后是 16,..,n/4,n/2,n,所以这些合并步骤的操作总数是 O(n),所以你在 O(n) 时间内得到第二个最接近的对。因此,您通过删除先前找到的对重复此 n/2 次,并获得总 O(n^2) 运行时和 O(nlogn) 额外空间来跟踪递归步骤,这会更好一些。

但是你可以做得更好,有一个随机数据结构可以让你在你的点集中进行更新并获得预期的 O(logn) 查询和更新时间。我对那个特定的数据结构不是很熟悉,但你可以在this paper 中找到它。这将使您的算法达到 O(nlogn) 的预期时间,我不确定是否存在具有类似运行时的确定性版本,但这些往往更麻烦。

【讨论】:

    【解决方案2】:

    我猜这个问题需要动态 Voronoi 图。

    当一个点集的Voronoi图已知时,可以在线性时间内找到最近邻对。

    然后删除这两个点可以在线性或亚线性时间内完成(我没有找到确切的信息)。

    因此,在全球范围内,您可以期待 O(N²) 的解决方案。

    【讨论】:

      【解决方案3】:

      如果您的距离是任意的,并且您不能将您的点嵌入欧几里得空间(和/或空间的维度会非常高),那么基本上没有办法绕过至少二次时间算法,因为您没有'在检查所有对之前,不知道最近的对是什么。很容易接近这一点,基本上是根据距离对所有对进行排序,然后维护一个布尔查找表,指示列表中的哪些点已经被采用,然后按顺序浏览排序对的列表并添加如果一对中的任何一个点都不在所取点的查找表中,则将一对点指向您的“最近邻居”,如果是,则将该对中的两个点都添加到查找表中。复杂度 O(n^2 log n),额外空间 O(n^2)。

      【讨论】:

        【解决方案4】:

        将所有点放入http://en.wikipedia.org/wiki/R-tree(时间O(n log(n))),然后为每个点计算到其最近邻居的距离。将点和初始距离放入优先队列。初始化一组空的已移除点和一组空的对。然后执行以下伪代码:

        while priority_queue is not empty:
            (distance, point) = priority_queue.get();
            if point in removed_set:
                continue
            neighbor = rtree.find_nearest_neighbor(point)
            if distance < distance_between(point, neighbor):
                # The previous neighbor was removed, find the next.
                priority_queue.add((distance_between(point, neighbor), point)
            else:
                # This is the closest pair.
                found_pairs.add(point, neighbor)
                removed_set.add(point)
                removed_set.add(neighbor)
                rtree.remove(point)
                rtree.remove(neighbor)
        

        其中最慢的部分是最近邻搜索。 R-tree 不保证那些最近邻搜索将是O(log(n))。但他们往往是。此外,您不能保证您将在每个点上进行O(1) 邻居搜索。但通常你会。所以平均性能应该是O(n log(n))。 (我可能遗漏了一个日志因素。)

        【讨论】:

        • 太棒了。我需要查一下。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2014-03-15
        • 2012-09-07
        • 2012-08-18
        • 2021-05-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多