最近点对问题

最近点对问题描述
对平面上给定的N个点,给出所有点对的最短距离,即,输入是平面上的N个点,输出是N点中具有最短距离的两点

题目要求:
要求随机生成N个点的平面坐标,应用穷举法和分治法编程计算出所有点对的最短距离

主要思路:
先要求用户输入点的个数(至少要2个点),然后生成横纵坐标范围为[-100,100]的点,points[i].x=(rand()%20000)/100-100;最后定义一个两点之间距离的计算公式。
穷举法的思路相对比较简单,枚举出所有可能的配对情况,然后一一比对,找到距离最小的点,输出即可。
分治法 情况1:点数小于等于二时:直接计算,求该两点之间的距离。情况2:集合中有三个点:两两比较,求三个点中的最近的两个点距离。情况3:点数大于三时:首先对点按照x升序,x相同则y升序排序,然后选中间下标的点划分集合S为SL和SR,分别在SL和SR中运用递归解决最近点对问题,得到DL和DR,分别表示SL和SR中的最近点对的距离。令d=min(DL,DR)。如果S中的最近点对(P1,P2)。P1、P2两点一个在SL和一个在SR中,那么P1和P2一定在以L为中心的间隙内,以L-d和L+d为界。将L-d—L+d内的点以y值排序,对于每一个点(x1,y1)找出y值在y1-d~y1+d内的接下来的7个点,计算距离为d’。如果d’小于d,令d=d’,最后的d值就是答案。(只找出7个点的原因是有结论:对于左边区域的每一点p,要想在右边区域找到一点p’使得pp’距离<=d,这样的点p’必定存在于【以p的y坐标为中心,上下延展d形成的d2d的子矩形中】 证明:假设极限情况,p在左右边线上,以p为中心半径为d画圆,半圆区域内,都是距p的距离小于d的,半圆是矩形的真子集,故目标点一定存在于矩形中,而且因为右侧的点具有稀疏性,即两两之间距离大于等于d, d2d的矩形区域,矩形区域内最多存在6个点(如果圆形区域则是四个))
经过验证,穷举法和分治法找出的最近点对相同。

#include <iostream>  
#include <ctime>
#include <cmath>
#include <algorithm>

using namespace std;  

#define NO_DISTANCE 1000000

//定义二维点Point
typedef struct Point 
{    
    float x,y;     //二维点的横纵坐标,范围均为[-100,100]
}Point;

//用随机函数对点数组points中的二维点进行初始化
void SetPoints(Point *points,int length)
{
    srand(unsigned(time(NULL)));  //设置随机种子
    for(int i=0;i<length;i++)
    {
        points[i].x=(rand()%20000)/100-100;    //调整rand(),使得横纵坐标范围为[-100,100]
        points[i].y=(rand()%20000)/100-100;
    }

}

//平面上任意两点对之间的距离公式计算
float Distance(Point a,Point b)
{
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

//自定义排序规则:依照结构体中的x成员变量升序排序
bool CmpX(Point a,Point b)
{
    return a.x<b.x;
}

//求出最近点对记录,并将两点记录再a、b中
float ClosestPair(Point points[],int length,Point &a,Point &b)
{
    float distance;        //记录集合points中最近两点距离 
    float d1,d2;           //记录分割后两个子集中各自最小点对距离
    int i=0,j=0,k=0;       //用于控制for循环的循环变量
    Point a1,b1,a2,b2;     //保存分割后两个子集中最小点对

    if(length<2)return NO_DISTANCE;    //若子集长度小于2,定义为最大距离,表示不可达
    if(length==2)
    {
        a=points[0];
        b=points[1];
        distance=Distance(points[0],points[1]);
    }
    else
    {
        Point *pts1=new Point[length];     //开辟两个子集
        Point *pts2=new Point[length];  

        sort(points,points+length,CmpX);   //调用algorithm库中的sort函数对points进行排序,CmpX为自定义的排序规则
        float mid=points[(length-1)/2].x;  //排完序后的中间下标值,即中位数

        for(i=0;i<length/2;i++)
            pts1[i]=points[i];
        for(int j=0,i=length/2;i<length;i++)
            pts2[j++]=points[i];
        d1=ClosestPair(pts1,length/2,a1,b1);           //分治求解左半部分子集的最近点  
        d2=ClosestPair(pts2,length-length/2,a2,b2);    //分治求解右半部分子集的最近点  
        if(d1<d2) { distance=d1; a=a1; b=b1;}
        else { distance=d2; a=a2; b=b2;}

        //求解跨分割线并在δ×2δ区间内的最近点对
        Point *pts3=new Point[length];   
        for(i=0,k=0;i<length;i++)
            if(abs(points[i].x-mid)<=distance)pts3[k++]=points[i];

        for(i=0;i<k;i++)
            for(j=i+1;j<=i+7&&j<k;j++)    //只需与有序的领接的的7个点进行比较
            {
                if(Distance(pts3[i],pts3[j])<distance)
                {//如果跨分割线的两点距离小于已知最小距离,则记录该距离
                    distance=Distance(pts3[i],pts3[j]);
                    a=pts3[i];
                    b=pts3[j];
                }
            }
    }
    return distance;
}

int main()
{
    int N;      //随机生成的点对个数
    Point a,b;
    Point c,d;
    float distance1;


    cout<<"请您输入二维点对个数:";
    cin>>N;
    if(N<2)
        cout<<"请输入大于等于2的点个数!!"<<endl;
    else
    {
        cout<<endl<<"随机生成的"<<N<<"个二维点对如下:"<<endl;
        Point *points=new Point[N];

        SetPoints(points,N);

        for(int i=0;i<N;i++)
            cout<<"("<<points[i].x<<","<<points[i].y<<")"<<endl;


float temp=0;          //穷举法找出最近点对
float min_dis=1000000000;
	for (int s=0; s<N-1; s++)
    {
          for(int t=s+1; t<N; t++)
          {
                
               temp=Distance(points[s],points[t]);
				   
                   if (temp<min_dis)
				   {
					   min_dis = temp;   //更新找到的最小距离
				   c=points[s];
                    d=points[t];
				   }
                
          }
    }
	cout<<"穷举法找出的最近点对是:"<<"("<<c.x<<","<<c.y<<")和"<<"("<<d.x<<","<<d.y<<")"<<endl<<"最近点对距离为:"<<min_dis<<'\n';



        distance1=ClosestPair(points,N,a,b);

        cout<<endl<<endl<<"按横坐标排序后的点对:"<<endl;
        for(int j=0;j<N;j++)
        {
            cout<<"("<<points[j].x<<","<<points[j].y<<")"<<endl;
        }
        cout<<endl<<"最近点对为:"<<"("<<a.x<<","<<a.y<<")和"<<"("<<b.x<<","<<b.y<<")"<<endl<<"最近点对距离为:"<<distance1<<endl;
    }
        system("pause");

}
  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最近问题是指在平面上给定N个点,求其中距离最近的两个点之间的距离。分治法是一种常用的解决最近问题算法。 分治法的基本思想是将问题分解成多个子问题,然后递归地解决这些子问题,最后将子问题的解合并起来得到原问题的解。对于最近问题,我们可以采用以下的分治算法: 1. 将所有按照横坐标从小到大排序,然后将它们划分为两部分,分别处理左右两个部分。 2. 对于左右两个部分,分别递归求解最近对距离。 3. 将左右两个部分中距离最近对的距离记为d。 4. 扫描所有横坐标在中间区域内的,按照纵坐标从小到大排序。对于每个点,只需考虑它与纵坐标差不超过d的之间的距离,因为这些是可能成为最近对的候选者。 5. 对于每个点,只需要考虑它与后面7个点之间的距离即可,因为如果有距离更近的,它们之间的距离一定小于d。 6. 扫描完中间区域内的所有后,最近对的距离就是左右两个部分中的最小值和中间区域内的最小值中的较小值。 下面是该算法的伪代码: ``` function closest_pair(points): // 按照横坐标排序 sort(points, key=lambda p: p.x) // 递归终止条件 if len(points) <= 3: return brute_force(points) // 求解左右两个部分的最近对距离 mid = len(points) // 2 left_points = points[:mid] right_points = points[mid:] left_min_dist = closest_pair(left_points) right_min_dist = closest_pair(right_points) // 求解中间区域的最近对距离 min_dist = min(left_min_dist, right_min_dist) strip_points = [p for p in points if abs(p.x - points[mid].x) < min_dist] strip_points.sort(key=lambda p: p.y) for i in range(len(strip_points)): for j in range(i+1, min(i+8, len(strip_points))): dist = distance(strip_points[i], strip_points[j]) min_dist = min(min_dist, dist) return min_dist ``` 其中,brute_force是暴力求解最近对距离的函数,distance是计算两个点之间距离的函数。时间复杂度约为O(N log N)。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值