找到最接近的点对 - 如何在递归侧对函数调用中实现拆分对

时间:2016-10-03 14:40:27

标签: c++ algorithm recursion computational-geometry divide-and-conquer

我正在尝试使用分而治之的方法来实现找到最接近的点算法。

首先说明一下,分割对(p1,p2)是最接近的对,使得p1在左侧,p2在右侧。侧对(p3,p4)是最接近的一对,使得p3和p4在一侧。

我已经完成了它返回正确结果的实现,但仅当closest_split_pairs函数被单独调用时(不在closest_side_pairs函数内)。所以,如果我提供以下几点:

2,1
8,3
5,8
9,1
5,2
3,3
4,5
6,5
1,9
2,1.5

我得到2,12,1.5的结果。

但是,如果我在closest_split_pair内调用closest_side_pairs(我认为应该是这样的话)。我得到错误的结果3,34,5。问题是我不知道在closest_split_pairs内集成closest_side_pairs需要做些什么。以下是在closest_split_pairs内调用closest_side_pairs的代码,并在main函数中对单独的调用进行了注释。

#include <iostream>
#include <array>
#include <algorithm>
#include <fstream>
#include <cfloat>
#include <cmath>

struct Point {
    Point(double x = 0, double y = 0) {
        x_coordinate = x;
        y_coordinate = y;
    }
    double x_coordinate;
    double y_coordinate;
    static bool sortByX(const Point &lhs, const Point &rhs) {
      return lhs.x_coordinate < rhs.x_coordinate;
    }
    static bool sortByY(const Point &lhs, const Point &rhs) {
      return lhs.y_coordinate < rhs.y_coordinate;
     }
};

using p_iterator = std::vector<Point>::iterator;

template<std::size_t SIZE>
using p_iterators_array = std::array<std::vector<Point>::iterator, SIZE>;

void initialize_points(std::vector<Point> &points) {
    double x, y;
    char c;
    std::ifstream infile("./points.txt");
    while((infile >> x >> c >> y) && (c == ',')) {
        points.push_back(Point(x, y));
    }
}

double calculate_distance(Point &p1, Point &p2) {
    return std::sqrt(std::pow(p1.x_coordinate - p2.x_coordinate, 2) + std::pow(p1.y_coordinate - p2.y_coordinate , 2));
}

template<typename T>
p_iterators_array<2> eucledian_closest(T &points, int size) {
    p_iterators_array<2> closest_arr;
    double closest_distance = DBL_MAX, distance = 0.0;

    for(int i = 0; i < size - 1; i++){
      for(int j = i + 1; j < size; j++) {
        distance = calculate_distance(points[i], points[j]);
        if(distance < closest_distance ) {
          closest_distance = distance;
          closest_arr[0] = points + i;
          closest_arr[1] = points + j;
        }
      }
    }
    return closest_arr;
}

p_iterators_array<2> closest_split_pair(p_iterator points_iterator, p_iterators_array<2> &closest_side_pairs, std::size_t size) {
    std::vector<p_iterator> split_pairs;
    p_iterators_array<2> final_result;
    double closest_distance = DBL_MAX, distance = 0.0;

    p_iterator midpoint = points_iterator + (size/2);

    //filtering points to only points in sigma-2sigma rectangle
    for (size_t i = 0; i < size; i++) {
      if(std::abs(points_iterator[i].x_coordinate - midpoint->x_coordinate) < calculate_distance(*(closest_side_pairs[0]), *(closest_side_pairs[1]))){
          split_pairs.push_back(points_iterator + i);
      }
    }

    //finding closest pair in split_pairs
    for (size_t i = 0; i < split_pairs.size() - 1; i++) {
      for (size_t j = i+1; (j < 7) && (j < split_pairs.size()) ; j++) {
        distance = calculate_distance(*(split_pairs[i]), *(split_pairs[j]));
        if(distance < closest_distance ) {
          closest_distance = distance;
          final_result[0] = split_pairs[i];
          final_result[1] = split_pairs[j];
        }
      }
    }

    //comparing split paris distance and side pairs distance
    if(calculate_distance(*(closest_side_pairs.front()), *(closest_side_pairs.back())) < calculate_distance(*(final_result.front()), *(final_result.back()))) {
      final_result = closest_side_pairs;
    }
    return final_result;
}

p_iterators_array<2> closest_side_pair(p_iterator points_iterator, p_iterator x_arr_iterator, p_iterator y_arr_iterator, std::size_t size) {
    std::size_t delimeter = size / 2 ;
    if(delimeter <= 3) {
      return eucledian_closest(points_iterator, delimeter);
    }
    p_iterators_array<2> closest_left, closest_right, result;

    closest_left = closest_side_pair(points_iterator, x_arr_iterator, y_arr_iterator, delimeter);
    closest_right = closest_side_pair(points_iterator + delimeter, x_arr_iterator + delimeter, y_arr_iterator + delimeter, delimeter);

    if(calculate_distance(*(closest_left.front()), *(closest_left.back())) < calculate_distance(*(closest_right.front()), *(closest_right.back()))) {
      result = closest_left;
    } else {
      result = closest_right;
    }
    return closest_split_pair(points_iterator, result, delimeter);
}

int main()
{
    std::vector<Point> points;
    initialize_points(points);

    std::vector<Point> x_p = points;
    std::vector<Point> y_p = points;
    std::sort(x_p.begin(), x_p.end(), Point::sortByX);
    std::sort(y_p.begin(), y_p.end(), Point::sortByY);

    p_iterators_array<2> closest_result = closest_side_pair(points.begin(), x_p.begin(), y_p.begin(), points.size());
    //Separate call of closest_split_pair
    //closest_result = closest_split_pair(points.begin(), closest_result, points.size());
    std::cout << "Closest pair are: " << std::endl;
    for(auto p: closest_result) {
        std::cout << p->x_coordinate << ", " << p->y_coordinate << std::endl;
    }
}

1 个答案:

答案 0 :(得分:2)

closest_side_pair例程中有几个错误。

当您调用eucledian_closest例程时,传递给该函数的向量长度为​​delimiter,而该值应为size。同样,当您调用closest_split_pair时,传递的向量长度为​​delimiter。那应该是size

目前closest_split_pair的作用是,它假设中点位于points_iterator + delimiter/2,您希望它位于points_iterator + size/2。这看起来很混乱,但只需将{delimiter'替换为closest-side_pair中的'size',您的代码就必须正常工作。