【问题标题】:Closest Pairs by sweeping vertically通过垂直扫描最接近的对
【发布时间】:2015-02-26 05:59:44
【问题描述】:

用于最近对的标准扫描线算法是众所周知的,如 here 所述,它使用扫描线水平扫描点集,仅保留当前点当前最佳距离内的点。

通常,点最初必须按 x 坐标排序,而边界框(在 c++ 实现的情况下为 std::set)必须按 y 坐标排序,如this c++ implementation 所示。

但是,在尝试实现时,我不小心忘记按 x 坐标对点进行排序,而是按 y 坐标对它们进行排序。令人惊讶的是,这似乎仍然有效。 你可以看到我的实现here,它基本上遵循标准线扫描最接近对算法的略微修改版本:

#include <iostream>
#include <set>
#include <algorithm>
#include <math.h>
#include <vector>

using namespace std;

#define x second
#define y first

typedef pair<long long, long long> pll;

inline double dist(pll p1, pll p2)
{
    return sqrt((double) (p2.y - p1.y)*(p2.y - p1.y) + (p2.x - p1.x)*(p2.x - p1.x));
}


int main(int argc, const char * argv[])
{
    int numPoints;
    cin >> numPoints;

    vector <pll> points;
    points.resize(numPoints);

    for (int i = 0; i < numPoints; i++)
    {
        cin >> points[i].x >> points[i].y;
    }

    //Sorts the points by y coordinate (because y is first)
    sort(points.begin(), points.end());

    double shortestDistSoFar = INFINITY;
    set <pll> boundingBox; //Bounding box maintained by y-coordinate
    boundingBox.insert(points[0]);

    int left = 0;

    pll best1, best2;

    for (int i = 1; i < numPoints; i++)
    {
        //Maintain only points to the left of the current point whose distance is less than bestDist
        while ((left < i) && (points[i].x - points[left].x > shortestDistSoFar))
        {
            boundingBox.erase(points[left]);
            left++;
        }

        //Consider only points within bestDist of the current point
        for (auto it = boundingBox.lower_bound(pll(points[i].y - shortestDistSoFar, points[i].x - shortestDistSoFar));
             it != boundingBox.end() && it->y <= points[i].y + shortestDistSoFar; it++)
        {
            if (dist(*it, points[i]) < shortestDistSoFar)
            {
                shortestDistSoFar = dist(*it, points[i]);
                best1 = *it;
                best2 = points[i];
            }
        }

        boundingBox.insert(points[i]);
    }

    return 0;
}

按照 y 坐标递增的顺序访问每个点,并为每个点检查从 y-bestDist 到 y+bestDist 的所有点,当找到新的最短距离时更新 bestDist 并从集合中删除其 x 坐标距离当前点大于 bestDist。

这个修改后的算法还能用吗(我只测试了几个案例),运行时间还是O(N lgN)吗?

【问题讨论】:

  • 从你的距离函数中删除 sqrt,它是不必要且昂贵的

标签: c++ algorithm computational-geometry


【解决方案1】:

它确实可以正常工作(因为只有在可以安全删除点时才会从集合中删除点)。但是,它具有O(n ^ 2) 时间复杂度,因为这些点并不总是在应该删除的时候被删除。

这个简单的生成器(用python3编写):

from sys import argv

n = int(argv[1])
dy = 1
dx = -n
print(n)
for i in range(n):
  print(dx * i, dy * i)

创建一个测试用例,使您的代码对任何n 执行O(n ^ 2) 操作。

这个测试用例的想法很简单:如果点(按 y 坐标排序后)以 x 坐标的降序迭代,它们永远不会从集合中删除。因此,如果它们在 y 坐标上彼此靠近而在 x 坐标上距离很远,则每次都会遍历整个集合。这就是检查所有点对的原因(并且正好有 n * (n - 1) / 2 对)。

现在让我们看看它在实践中是如何工作的:
首先,我用g++ -std=c++11 -O2 closest_pair.cpp 编译了你的代码。 之后我进行了一系列测试:

temp$ python3 gen.py 10000 > input
temp$ time ./a.out < input

real    0m0.805s
user    0m0.797s
sys     0m0.008s

temp$ python3 gen.py 30000 > input
temp$ time ./a.out < input

real    0m7.195s
user    0m7.198s
sys     0m0.004s

temp$ python3 gen.py 50000 > input
temp$ time ./a.out < input

real    0m23.711s
user    0m23.725s
sys     0m0.004s

如您所见,它的运行速度非常慢。

【讨论】:

    猜你喜欢
    • 2023-01-16
    • 2015-03-07
    • 2010-11-14
    • 1970-01-01
    • 2021-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多