最近点对问题

该问题的形式化表示:
Input:平面上有n个点
Output:欧式距离最近的点对

1 暴力破解法,列举出这个平面所有的点对,找出最小的那个点对,所需要的时间复杂度为O(n^2)

2 用分而治之的思想
(1) 将平面上的点均分成左右两部分
这里写图片描述

分别求出左,右两边最近点对的距离d1,d2,δ =min(d1,d2)
这里写图片描述

然后我们需要考虑的是,是否存在来自分割线左右两侧的点其距离比 δ 还小,所以也需要计算分割线左右两侧的点组成的点对,如果将左右两侧的所有点对列举出来,时间开销为O(n^2)

这里写图片描述

但是,在算法中由于我们的目标是找到比 δ 还小的点对,所以我们只需要计算L区域内的点,L区域的宽度为 2δ
这里写图片描述

进一步分析
是否L区域内的所有点我们都需要进行计算,我们将L条带进行分割,如下图,格子的边长为 δ/2 ,显然一个格子中最多有一个点,因为如有两个点落在同一个格子中,则这两点的距离小于 δ 。
这里写图片描述

同样的我们在y轴方向进行分析,对于点 i 也只需要在2rows的区域内进行计算。因此对于任意一个L区域内分割线左边的点,其实最多只需要计算右边6个点即可。
这里写图片描述这里写图片描述

这里写图片描述这里写图片描述

代码如下

#include <iostream>
#include <math.h>
#include <algorithm>
#include <vector>;
#include <limits.h>
using namespace std;

#define MIN(a,b) ((a)<(b)? (a):(b))

struct point {
    double x , y ;
}v[10000] ;

//存储L区域内的点
int v_strip[10000];

//计算两点之间的欧式距离
inline double dis( point a, point b ) {
    return sqrt ( (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) ) ;
}

int comx(const point &a,const point &b){
    return a.x < b.x ;
}

int comy(const int &a,const int &b){
    return v[a].y < v[b].y ;
}

//分治法求最近点对
//先将所有的点在x轴方向上排序
double ClosestPairPre(int n) {
    sort(v, v+n, comx);
    return ClosestPair(0, n-1) ;
}

double ClosestPair (int low, int high) {
    if ( (high-low)==0 ) {
        return INT_MAX;
    }
    if ( (high-low)==1 ) {
        return dis(v[low],v[high]) ;
    }
    //divide and conquer 
    int mid = (low+high)>>1 ;
    double dis_left = ClosestPair(low, mid) ;
    double dis_right = ClosestPair(mid+1, high) ;
    double dis_min = MIN(dis_left,dis_right) ;

    //record points in the 2*dis_min strip by x-coordinate
    int strip_low=0, strip_num=0 ;
    for (int i = low; i <= high; ++i) {
        if ( abs(v[i].x-v[mid].x) <= dis_min ){
            v_strip[strip_num++] = i;
        }
    }

    sort(v_strip, v_strip+strip_num, comy);

    //在strip中对任何一点,只需要和另一边的6个比较
    for (int i = strip_low; i < strip_num; ++i){
        int k=(i+7 > strip_num) ? strip_num:(i+7) ;
        for (int j = i+1; j < k; ++j){
            if ( ( v[v_strip[j]].y-v[v_strip[i]].y)>dis_min )
                break;
            dis_min = MIN(dis_min, dis(v[v_strip[i]], v[v_strip[j]])) ;
        }
    }
    return dis_min;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值