Search code examples
c++vectorpointdivide-and-conquerclosest-points

Closest pair of points Divide&Conquer with vectors


So I have decided to implement an algorithm based on vectors to solve the Closest pair of points problem (2D). It seems to work with easy cases like

1.2 4.5

2.4 1.2

3.3 1.1

4.4 4.4

7.7 1.1

1.1 2.1

8.6 1.9

3.3 9.0

And the output is correct (for this case it is 0.90554), but somebody has checked my code and said that there is an invalid memory reference somewhere. I've been really struggling with this piece of code but I've give up, there is no way to find what is wrong (bc the cases I try work!). I would really appreciate if somebody could enlighten me! Thanks in advance!

#include <iostream>
#include <algorithm>
#include <vector>
#include <cmath>

using namespace std;

struct Point { double x, y; };
bool compareX (Point p, Point q) { return p.x < q.x; }
bool compareY (Point p, Point q) { return p.y < q.y; }
float dist(Point p1, Point p2) {
    return sqrt((p1.x - p2.x)*(p1.x - p2.x) + (p1.y - p2.y)*(p1.y - p2.y));
}

double min_dist (vector<Point> pointsX, vector<Point> pointsY, int n) {
     // BASE CASE.
    if (n <= 3) {
        double min = __DBL_MAX__;
        for (int i = 0; i < n; ++i)
            for (int j = i +1 ; j < n; ++j)
                if (dist(pointsX[i], pointsX[j]) < min)
                    min = dist(pointsX[i], pointsX[j]);
        return min;
    }

    // Step 1: Find the middle point.
    int mid = n/2;
    Point mid_point = pointsX[mid];

    // Step 2: Divide the set in two equal-sized parts (left and right).
    vector<Point> pointsY_left;
    vector<Point> pointsY_right;
    vector<Point> pointsX_left;
    vector<Point> pointsX_right;

    for (int i = 0; i < n; ++i) {
        if (i < mid  and pointsY[i].x <= mid_point.x)  pointsY_left.push_back(pointsY[i]);
        else pointsY_right.push_back(pointsY[i]);
    }
    for (int i = 0; i < n; ++i) {
        if (i < mid and pointsX[i].x <= mid_point.x) pointsX_left.push_back(pointsX[i]);
        else pointsX_right.push_back(pointsX[i]);
    }

    // Step 3: Calculate the smaller distance at left and right parts recursively.
    double d_left = min_dist(pointsX_left, pointsY_left, mid);
    double d_right = min_dist(pointsX_right , pointsY_right, n - mid);

    // Let d be the minimal of the 2 distances.
    double d = min (d_left, d_right);

   // Eliminate points that are farther than d <=> Create a strip that contains
   // points closer than d.
   vector<Point> strip;
   for (int i = 0; i < n; ++i) if (abs(pointsY[i].x - mid_point.x) < d) strip.push_back(pointsY[i]);

   // Scan the points from the strip and compute the dstances of each point to its 7 neighbours.
   // Pick all points one by one and try the next points until the difference
   // between y coordinates is smaller than d.
   for (int i = 0; i < int(strip.size()); ++i)
        for (int j = i + 1; j < int(strip.size()) and (strip[j].y - strip[i].y) < d; ++j)
             if (dist (strip[i], strip[j]) < d)
             d = dist(strip[i], strip[j]);

     return d;
}

double closest(const vector<Point>& points) {
    // Initial step: sort points accroding to their coordinates.
    vector<Point> pointsX, pointsY;
    pointsX = pointsY = points;
    sort(pointsX.begin(), pointsX.end(), compareX);
    sort(pointsY.begin(), pointsY.end(), compareY);

    return min_dist (pointsX, pointsY, int(points.size()));
}

int main() {
    cout.setf(ios::fixed);
    cout.precision(5);

    vector<Point> points;
    double x, y;
    while (cin >> x >> y) {
        Point p = {x, y};
        points.push_back(p);
    }

    cout << closest (points) << endl;
}

Solution

  • Looks like problem is in Step 2. You have two different conditions i < mid and pointsY[i].x <= mid_point.x and i < mid and pointsX[i].x <= mid_point.x, but in the following code you suppose these conditions are equal. Probably you should replace these conditions with just i < mid.

    // Step 1: Find the middle point.
    int mid = n/2;
    Point mid_point = pointsX[mid];
    
    // Step 2: Divide the set in two equal-sized parts (left and right).
    vector<Point> pointsY_left;
    vector<Point> pointsY_right;
    vector<Point> pointsX_left;
    vector<Point> pointsX_right;
    
    for (int i = 0; i < n; ++i) {
        if (i < mid  and pointsY[i].x <= mid_point.x)  pointsY_left.push_back(pointsY[i]);
        else pointsY_right.push_back(pointsY[i]);
    }
    for (int i = 0; i < n; ++i) {
        if (i < mid and pointsX[i].x <= mid_point.x) pointsX_left.push_back(pointsX[i]);
        else pointsX_right.push_back(pointsX[i]);
    }
    

    P.S. You can simply use constructor of vector:

    vector<Point> pointsY_left( pointsY.cbegin(), pointsY.cbegin() + mid );
    vector<Point> pointsY_right( pointsY.cbegin() + mid, pointsY.cend() );
    vector<Point> pointsX_left( pointsX.cbegin(), pointsX.cbegin() + mid );
    vector<Point> pointsX_right( pointsX.cbegin() + mid, pointsX.cend() );