要求:
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)求得左右两边的子集的最近点对很容易,难点在于可能最近点对的点,一个在左子集,另一个在右子集。我们可以采取以下措施化简运算:
-
只考虑与垂直线相距dis_min区间的点,将范围内的点加入数组t[n]中,超过这范围的点距离不可能小于dis_min。但当点比较密集时,都靠近这个垂直线,最坏时间复杂度仍为o(n2)。
-
只考虑每个点和它后面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;
}