最近点对问题 |8月更文挑战

250 阅读3分钟

要求:

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

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

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

实验过程:

采用分治的方法。

基本思想:

将S分成不相交的两个子集,求每个子集的最近点对,以及分别位于两个子集中的所有点的最近点对,最后通过比较得到S中的最近点对。

具体步骤:

集合用数组s[1..n]表示。

(1)n=2,3时直接求解

(2)n>3时分解,在递归前就将数组采用快速排序排好序,能避免递归过程中排序占用资源。通过过s[2/n]的垂直线将数组分为左右两个子集Sl,Sr,再分别对它们递归,得到dis_l,dis_r。取两者中最小值给dis_min。

(3)求得左右两边的子集的最近点对很容易,难点在于可能最近点对的点,一个在左子集,另一个在右子集。我们可以采取以下措施化简运算:

  1. 只考虑与垂直线相距dis_min区间的点,将范围内的点加入数组t[n]中,超过这范围的点距离不可能小于dis_min。但当点比较密集时,都靠近这个垂直线,最坏时间复杂度仍为o(n2)。

  2. 只考虑每个点和它后面7的点的距离。

代码部分:

#include <iostream>
#include <math.h>
using namespace std;
const int n=8;

struct point
{
    double x,y;
};

int Partition_x(point r[],int left,int right);//按x坐标划分
int Partition_y(point r[],int left,int right);//按y坐标划分
void QuickSort_x(point r[],int left,int right);//x坐标快速排序
void QuickSort_y(point r[],int left,int right);//y坐标快速排序
double Distance(point a,point b);//求解两个点坐标

//按x坐标划分
int Partition_x(point r[],int left,int right){
    point pivot=r[left];
    while(left<right){
        while(left<right&&r[right].x>=pivot.x)
             right--;
             if(left<right){
             point temp=r[left];r[left]=r[right];r[right]=temp;
             left++;
             }
        while(left<right&&r[left].x<=pivot.x)
             left++;
             if(left<right){
             point temp=r[left];r[left]=r[right];r[right]=temp;
             right--;
             }
    }
    return left;
}

//x坐标快速排序
void QuickSort_x(point r[],int left,int right){
    if(left<right){
        int q=Partition_x(r,left,right);
        QuickSort_x(r,left,q-1);
        QuickSort_x(r,q+1,right);
    }
}

//按y坐标划分
int Partition_y(point r[],int left,int right){
    point pivot=r[left];
    while(left<right){
        while(left<right&&r[right].y>=pivot.y)
             right--;
             if(left<right){
             r[left]=r[right];
             }
        while(left<right&&r[left].y<=pivot.y)
             left++;
             if(left<right){
             r[right]=r[left];
             }
    }
    r[left]=pivot;
    return left;
}

//y坐标快速排序
void QuickSort_y(point r[],int left,int right){
    if(left<right){
        int q=Partition_y(r,left,right);
        QuickSort_y(r,left,q-1);
        QuickSort_y(r,q+1,right);
    }
}

//求解两点距离
double Distance(point a,point b){
    return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));
}

//求解两数最小值
double min(double x,double y){
   if(x>y)
   x=y;
   return x;
}

//求解最近点对
double  cp(point s[],int low,int high){
    int k=0;
    int mid;
    int x0;
    double d1,d2,d3;
    double dis_l,dis_r,dis_min,dis_min_temp;
    point t[n];//设立一个数组t[n]用来保存按y排序的数组
    if (high-low == 1) {  //当仅有两点的情况
		return Distance(s[low], s[high]);
	}
    else if(high-low==2){    //当有三个点的情况
        d1=Distance(s[low],s[low+1]);
        d2=Distance(s[low+1],s[high]);
        d3=Distance(s[low],s[high]);
        if(d1>d2) d1=d2;
        if(d1>d3) d1=d3;
        return d1; 
     }
     else{
     mid=(low+high)/2;
     x0=s[mid].x;   //获得中点的坐标
     dis_l=cp(s,low,mid);  //递归左子集获得左侧最近点对距离
     dis_r=cp(s,mid+1,high);//递归右子集获得右侧最近点对距离
     dis_min=min(dis_l,dis_r); //最近点对距离为两者最小值
     QuickSort_y(s,low,high);  //按y排序数组
     for(int i=0;i<n;i++){
         if(fabs(s[i].x-x0)<=dis_min){//对于数组中与中点距离小于di_min的点加入到t[n]中,进行后续的比较
         t[k++]=s[i];
         }
     }
     dis_min_temp=dis_min;
     for(int i=0;i<k-1;i++){//对于t[n]中每个点计算它们与其后7个点的距离,若是小于dis_min_temp,则更新dis_min_temp
         for(int j=i+1;j<min(i+7,k);j++){//用min(i+7,k)是因为靠数组后面的节点后面不够7个点
             if(Distance(t[i],t[j])<dis_min_temp)
             dis_min_temp=Distance(t[i],t[j]);
         }
     }
     dis_min=dis_min_temp;
     }
     return dis_min;
}


int main(){
    point s[n]={{1,2},{2,1},{3,2},{4,4},{5,1},{6,6},{7,2}};//,{2,1},{3,2},{4,4},{5,1},{6,6},{7,2}
    QuickSort_x(s,0,n-1);//先对数组按x排序
    double minDist=cp(s,0,n-1);
    cout<<"The smallest distance is: "<<minDist<<endl;
    system("pause");
    return 0;
}