分治法之最近对问题(附带实验截图和举例分析)

一、问题描述

设p1=(x1, y1), p2=(x2, y2), …, pn=(xn, yn)是平面上n 个点构成的集合S,最近对问题就是找出集合S中距离最近的点对。
严格地讲,最近的点对可能多于一对,简单起见,只找出其中的一对作为问题的解。

二、最近对的分治策略

2.1 一维情况

2.1.1解法

为了使问题易于理解,先考虑一维的情形。此时,S中的点退化为 x 轴上的n个点x1, x2, …, xn。用x 轴上 的某个点 m 将 S 划分为两个集合S1和S2,并且S1和S2 含有点的个数相同。递归地在S1和S2上求出最接近点 对 (p1, p2) 和(q1, q2),如果集合S中的最接近点对都在子集S1或S2中,则d=min{(p1, p2), (q1, q2)}即为所求, 如果集合S中的最接近点对分别在S1和S2中,则一定 是(p3, q3),其中:p3是子集S1中的最大值,q3是子集 S2中的最小值。在这里插入图片描述

2.1.2 m点的选取问题

按这种分治策略求解最近对问题的算法效率取决于划分点m的选取,一个基本的要求是要遵循平衡子问题的原则。如果选取m=(max{S}+min{S})/2,则 有可能因集合S中点分布的不均匀而造成子集S1和S2 的不平衡,如果用S中各点坐标的中位数(即S的中值)作为分割点,则会得到一个平衡的分割点m, 使得子集S1和S2中有个数大致相同的点。在代码中的实现方法是先将集合S中的点的x坐标做一个升序排列,再设定low=0,high=s.length-1;计算low和high的中间值mid.作为中间的分割线。

2.2 二维情况

2.2.1 最近对的三种区间

接下来考虑二维的情形, 此时S中的点为平面上的点。为了将平面上的点集S 分割为点的个数大致相同的两个子集S1和S2,选取垂直线 x=m来作为分割线,其中,m为S中各点x坐标的中位数。由此将S分割为S1={p∈S | xp≤m}和S2={q∈S | xq>m}。递归地在S1和S2上求解最近对问题,分别得到S1中的最近 距离d1和S2中的最近距离d2,令d=min(d1, d2);若S的最近对(p, q)之间的距离小于d,则p和q必分属于 S1和S2,不妨设p∈S1,q∈S2,则p和q距直线x=m的 距离均小于d,(pq<d,pm,mq肯定都小于d.)所以,可以将求解限制在以x=m为中 心、宽度为2d的垂直带P1和P2中,垂直带之外的任何点对之间的距离都一定大于d。
在这里插入图片描述

2.2.2 对区间S2的选点规则

对于点p∈P1,需要考察P2中的各个点和点p之间的距离是否小于d,显然,P2中这样点的y轴坐标一定位于区间[y-d, y+d] 之间 ,(以p为圆心画一个直径为d的圆,最近的点在里面。所以y的上下范围是y-d,y+d,这里的y是p的y值)。而且,这样的点不会超过6个(因P2中点与点之间的距离不能小于d,有相关几何定理已经证明)。故可将P1和 P2中的点按照y轴坐标值升序排列,顺序处理P1中的点p,同时在P2中选出符合条件的候选点(最多6个),计算它们与点 p之间的距离。
在这里插入图片描述

情况a:
正常情况下点少于6个,此时依次边筛选符合的点(y值符合)边计算两点间的距离。在事先筛选出x值在m-d,m+d范围内的点后再筛选y值在y-d~y+d的范围的点,边筛选边计算距离,例如考察A点的y值是否在范围内,在范围内以后就计算PA的距离,再将这个距离和之前算出的最小距离比较,比最小的小就赋值给最小的,否则就检查下一个点。直到所有的点都检查完毕。此时也得出了整个区间的最小距离.
情况 b:最坏情况是点均在边上,根据组合数学鸽笼原理,可以知道符合条件的点最多不超过6个点。而且如果是6个点,这6个点一定在边上,因为要求每个点间的距离大于d
如下图中的点数大于6个的情况不会出现。
因为GF距离比d小,所以不可能出现在范围内。 而且此时该区间中位数的数组下标值也不再是x=m,需要重新调整。

在这里插入图片描述
让我们来先看看代码

2.2.3 代码分析

点的存储结构
/*

点的存储结构

*/
struct point
{
  int x, y;
};

随机生成点

/*
随机生成点
*/
void Initial(int N, point* p) 
{    srand(time(0));  //种子为数据的规模  
     int i;    
    for (i = 0; i < N; i++) {  
//产生随机数,X集和Y集中点的内容是一样的,只是排序的方法不同而已        
          p[i].x = rand()%(10-1)+1;        
          p [i].y = rand()%(10-1)+1;        
}
}
求两点之间距离

/*
求两点之间距离

*/
double Distance(point a, point b)
{
  return sqrt(double((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)));
}

求最近对函数
double Closest_DC(point S[ ], int low, int high)/*由于本身会迭代,所以无论是传递结构体指针还是在内部直接打印,最后都不能正确打印出最近点对的坐标值,在这里就不加这个东西了。*/
{
    QuickSortx(S, 0, high);//表示将集合S中的点的x值按照从0到high值进行升序排序。
    double d1,d2,d3,d;
    int mid,i,j,index;
    point P[100];//用于存放S1,S2
    if (high-low==1)//如果数组区间长度为1,说明只有两个点,则直接返回。
    {
     return Distance(S[low], S[high]);
    }
    
    if(high-low==2){//如果有三个点ABC,分别算AB BC AC距离比较,然后选出最小的值返回
        d1=Distance(S[low], S[low+1]);
        d2=Distance(S[low+1], S[high]);
        d3=Distance(S[low], S[high]);
        if((d1<d2)&&(d1<d3))
          return d1;
        
     else if (d2<d3)
         
             return d2;
        
        
        else
       
            return d3;
        
        
    }
    mid=(low+high)/2;//如果是4个点及以上
    d1
  • 6
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
以下是使用分治法解决最近问题的 C++ 代码: ```c++ #include <iostream> #include <algorithm> #include <cmath> using namespace std; // 计算两个数的最小距离,其中a[]已排序 double closestInStrip(double strip[], int size, double d) { double minDist = d; for (int i = 0; i < size; ++i) { for (int j = i + 1; j < size && (strip[j] - strip[i] < minDist); ++j) { minDist = min(minDist, strip[j] - strip[i]); } } return minDist; } // 计算最近对的距离 double closestPair(double x[], double y[], int n) { if (n <= 3) { double dist = INFINITY; for (int i = 0; i < n; ++i) { for (int j = i+1; j < n; ++j) { dist = min(dist, sqrt(pow(x[i]-x[j], 2) + pow(y[i]-y[j], 2))); } } return dist; } int mid = n / 2; double midX = x[mid]; double leftDist = closestPair(x, y, mid); double rightDist = closestPair(x+mid, y+mid, n-mid); double dist = min(leftDist, rightDist); // 计算跨越中线的最近对的距离 double strip[n]; int j = 0; for (int i = 0; i < n; ++i) { if (abs(x[i] - midX) < dist) { strip[j++] = y[i]; } } sort(strip, strip+j); return min(dist, closestInStrip(strip, j, dist)); } int main() { int n = 6; double x[] = {2, 12, 40, 5, 12, 3}; double y[] = {3, 30, 50, 1, 10, 4}; sort(x, x+n); cout << closestPair(x, y, n) << endl; // 输出 2.82843 return 0; } ``` 其中,函数 `closestPair` 采用分治法实现,递归地计算左右两部分的最近对距离,然后计算跨越中线的最近对距离。函数 `closestInStrip` 计算给定长度的数组中的最近对距离。在主函数中,我们随机生成了一组 6 个,然后调用 `closestPair` 计算最近对距离。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值