一、 算法设计与分析:
采用分治算法,算法每一次递归调用的输入为点集的子集P,以及数组X和数组Y,由于点击P按X坐标划分,所以不用单独输入X,采用Point.x即可,对数组X中的点排序,使其x坐标坐标单调递增。类似地,对数组Y中的点排序,使其y坐标单调递增。不能再每次递归调用中都进行排序,采用预排序的策略,对Y数组预排序伪代码如下:
Let YL[1…Y.length]and YR[1…Y.length] be new arrays
YL.length=YR.length=0
for i=1 to Y.length
if Y[i] is in PL
YL.length=YL.length+1
YL[YL.length]=Y[i]
else
YR.length=YR.length+1
YR[YR..length]=Y[i]
该算法涉及的数据结构如下:
//点结构 struct Point{ Point(){} Point(double x, double y){this->x=x;this->y=y;} double x;//x坐标 double y;//y坐标 };
//Y坐标定义 struct axisY{ int ID;//点编号 double Yvalue;//y坐标 };
二、算法实现
//点定义 struct Point{ Point(){} Point(double x, double y){this->x=x; this->y=y;} double x;//x坐标 double y;//y坐标 }; //Y坐标定义 struct axisY{ int ID;//点编号 double Yvalue;//y坐标 }; // bool cmpY(axisY y1, axisY y2){ return y1.Yvalue<y2.Yvalue; } bool cmpPoint(Point p1, Point p2){ return p1.x<p2.x; } #include "compare.h" #define MAX_NUM 1000 #include <iostream> #include <cmath> #include <climits> #include <algorithm> using namespace std; double getMinDis(Point points[], axisY Y[], int left, int right, Point& point1, Point& point2); double getDisLessThree(Point points[], int start, int end, Point& point1, Point& point2); double getMinCross(Point points[], axisY Y_2d[],int count, Point& point1, Point& point2); double getDistance(Point p1,Point p2); int main(){ double min; Point points[MAX_NUM];//存储所有点,排序后下标表示ID //axisX X[MAX_NUM];//存储所有点x坐标 axisY Y[MAX_NUM];//存储所有点y坐标 Point point1,point2; for(int i=0;i<MAX_NUM;i++){ points[i].x=(rand()/double(RAND_MAX))*100; points[i].y=(rand()/double(RAND_MAX))*100; } /* //测试数据 points[0].x=9.83;points[0].y=-81.96; points[1].x=-88.29;points[1].y=44.76; points[2].x=21.97;points[2].y=-81.49; points[3].x=2.44;points[3].y=-1.83; points[4].x=-89.17;points[4].y=63.58; points[5].x=20;points[5].y=-49.92; points[6].x=-81.21;points[6].y=-48.01; points[7].x=-33.28;points[7].y=-49.09; points[8].x=-54.05;points[8].y=12.88; points[9].x=-64.85;points[9].y=-53.12; points[10].x=12.07;points[10].y=64.91; points[11].x=-72.9;points[11].y=-21.57; points[12].x=12.93;points[12].y=-92.71; points[13].x=-27.71;points[13].y=-0.19; points[14].x=73.17;points[14].y=32.17; */ sort(points,points+MAX_NUM,cmpPoint);// points按x坐标有序 for(int i=0;i<MAX_NUM;i++){ Y[i].ID=i; Y[i].Yvalue=points[i].y; } sort(Y,Y+MAX_NUM,cmpY); min=getMinDis(points, Y, 0, MAX_NUM-1,point1, point2); cout << "最近点对为:("<< point1.x << "," << point1.y << ")和"<<"("<<point2.x<< "," << point2.y << ")"<< endl; cout << "最小距离:"<< min << endl; return 0; } //返回最小距离 double getMinDis(Point points[], axisY Y[], int left, int right, Point& point1, Point& point2){ int num_point; int mid; double min,min_left,min_right,min_cross; int lenL,lenR; Point *PL,*PR; axisY *YL,*YR,*Y_2d; double xleft,xright; num_point=right-left+1; Y_2d=new axisY[num_point];//为Y_2d分配存储空间 if(num_point<=3){ //递归出口 return getDisLessThree(points, left, right,point1,point2); } mid=(left+right)/2; lenL=mid-left+1; lenR=right-mid; PL=new Point[lenL]; YL=new axisY[lenL]; PR=new Point[lenR]; YR=new axisY[lenR]; //求PL for(int i=left,j=0;i<=mid;i++){ PL[j++]=points[i]; } //求PR for(int i=mid+1,j=0;i<=right;i++){ PR[j++]=points[i]; } //求YL和YR for(int i=0,jl=0,jr=0;i<num_point;i++){ if(Y[i].ID<=mid) //插入YL YL[jl++]=Y[i]; else YR[jr++]=Y[i]; //插入YR } //递归求解 Point point_L1,point_L2; Point point_R1,point_R2; min_left=getMinDis(points, YL, left, mid, point_L1, point_L2); min_right=getMinDis(points,YR,mid+1,right, point_R1, point_R2); min=min_left;point1=point_L1;point2=point_L2; if(min_right<min){ min=min_right;point1=point_R1;point2=point_R2; } //求Y_2d int count=0; xleft=points[mid].x-min; xright=points[mid].x+min; for(int i=0;i<num_point;){ if((points[Y[i].ID].x<xleft) || (points[Y[i].ID].x>xright)){ i++; } else{ Y_2d[count++]=Y[i]; i++; } } //求min_cross; Point point_M1,point_M2; min_cross=getMinCross(points, Y_2d, count, point_M1, point_M2); if(min_cross<min){ point1=point_M1;point2=point_M2;min=min_cross; } return min; } //返回交叉区域最短距离 double getMinCross(Point points[], axisY Y_2d[],int count, Point& point1, Point& point2){ double min_across=DBL_MAX; double this_min; double this_dis; int i=0,j=0; int p1,p2; Point cur_point1,cur_point2; if(count==0){ return min_across; } for(i=0;i<count;i++){ this_min=DBL_MAX; p1=Y_2d[i].ID; cur_point1=points[p1]; cur_point2=points[p1]; for(j=1;j<8&&(i+j)<count;j++){ p2=Y_2d[i+j].ID; this_dis=getDistance(points[p1], points[p2]); if(this_dis<this_min){ this_min=this_dis; cur_point2=points[p2]; } } if(this_min<min_across){ min_across=this_min; point1=cur_point1; point2=cur_point2; } } return min_across; } //求两点p1和p2之间的距离 double getDistance(Point p1,Point p2){ double distance; distance=sqrt(pow(p1.x-p2.x,2)+pow(p1.y-p2.y,2)); return distance; } //当点数小于等于三点时暴力求解 double getDisLessThree(Point points[], int start, int end, Point& point1, Point& point2){ int len; double min; len=end-start+1; if(len==1){ min=-1; } else if(len==2){//有两个点 min=getDistance(points[start], points[end]); point1=points[start]; point2=points[end]; } else{//有三个点 double dis1,dis2,dis3; dis1=getDistance(points[start],points[start+1]); dis2=getDistance(points[start],points[end]); dis3=getDistance(points[start+1],points[end]); min=dis1; point1=points[start];point2=points[start+1]; if(dis2<min){ min=dis2; point1=points[start];point2=points[end]; } if(dis3<min){ min=dis3; point1=points[start+1];point2=points[end]; } } return min; }
三、实验结果分析
先采用经过如下15个点进行测试,如果结果正确最邻近点应为points[12]和
points[0],最小距离为11.1881
points[0].x=9.83;points[0].y=-81.96;
points[1].x=-88.29;points[1].y=44.76;
points[2].x=21.97;points[2].y=-81.49;
points[3].x=2.44;points[3].y=-1.83;
points[4].x=-89.17;points[4].y=63.58;
points[5].x=20;points[5].y=-49.92;
points[6].x=-81.21;points[6].y=-48.01;
points[7].x=-33.28;points[7].y=-49.09;
points[8].x=-54.05;points[8].y=12.88;
points[9].x=-64.85;points[9].y=-53.12;
points[10].x=12.07;points[10].y=64.91;
points[11].x=-72.9;points[11].y=-21.57;
points[12].x=12.93;points[12].y=-92.71;
points[13].x=-27.71;points[13].y=-0.19;
points[14].x=73.17;points[14].y=32.17;
运行结果如下:
![]()
和预期结果一致。
(2)采用如下语句随机生成100对点,寻找其最近点距离:
for(inti=0;i<MAX_NUM;i++){
points[i].x=(rand()/double(RAND_MAX))*100;
points[i].y=(rand()/double(RAND_MAX))*100;
}
结果如下:
![]()
(3)随机生成1000对点(坐标不超100)并进行测试,结果如下:
![]()
四、总结
根据事先设计好的数据对代码进行测试,测试结果和预期一致,证明了算法的正确性,加大测试数据集,分别采用100对随机点对和1000对随机点对进行测试,发现都能在很快的时间内,证明该算法是极其高效的。
该算法实现过程中,最巧妙的处理是对每个点,只需测试他和后边7个点之间的距离,以及对y坐标进行预排序,大大降低了时间复杂度。该算法实现的难点是对递归的处理,以及在递归调用过程中最近点对的跟踪。经过多次调试,上述问题都得到了解决。