最近点对问题
二维空间最近点对问题的分治算法伪代码

间的最近距离。
常用的解决方式是分治算法。
以下是分治算法的伪代码表示:假设我们的输入是一组点集,我们将它划分为几个部分,然后在每一部分内应用我们的搜索策略,最后将结果合并以得到整个点的最接近对。
```pythonfunction KPPSearch(points, k)if points is empty or k is 1return (points[0], points[0])else if k is 2return min(minDistance(points[left], points[right]), minDistance(points[right], points[middle]))mid = partition(points)closest = minDistance(points[mid], points[left])if k is 3 or closest is greater than 0return (mid, left)elseleftLeft = minDistance(points[left], points[leftLeft])leftRight = minDistance(points[left], points[leftRight])if leftLeft is greater than 0 and leftRight is greater than 0return (leftLeft, leftRight)elsereturn (left, leftRight)function partition(points)pivot = points[0]left = []middle = []right = []for point in points:if point is less than pivot in Euclidean distance:append(point to left)else:append(point to middle)for point in middle:append(point to right)return pivot, left, middle, right```上述伪代码中的主要步骤包括:分治法的核心步骤`partition`函数用于将数据集分为三部分,并且根据选择的"轴"进行划分;然后在每一个部分内,我们会使用这个方法进行搜索。
最接近点对问题实验报告

最接近点对问题一.实验目的:1.理解算法设计的基本步骤及各步的主要内容、基本要求;2.加深对分治设计方法基本思想的理解,并利用其解决现实生活中的问题;3.通过本次实验初步掌握将算法转化为计算机上机程序的方法。
二.实验内容:1.编写实现算法:给定n对点,在这n对点中找到距离最短的点对。
2.将输出数据存放到另一个文本文件中,包括结果和具体的运行时间。
3.对实验结果进行分析。
三.实验操作:1.最接近点对查找的思想:首先,将所有的点对按照x坐标排序,找到x坐标的中位数,将所有的点对分成三部分,横坐标小于x(S1)、等于x(S2)和大于x(S3)的点对,在求取每部分中的最短距离,利用分治法,一步步地分解为子问题,找到最短距离d。
由于距离最近的两个点可能在不同的区域中,需要进一步判断。
选择S1中的一个点,由于与它相比较的点的距离不可能超过d,故其配对范围为d*2d的矩形,将这个矩形划分为6份2/d*3/d的小矩形,其对角线的长度为5/6d,小于d,故S1中的任意一个点只需和S2中的6个点比较即可,最终确定最短的距离。
2.取中位数:为了减少算法的时间开销,需要将所有的点对进行分组,以中位数为基准,考虑到快速排序的不稳定性,本次排序使用了合并排序。
代码实现:template <class Type>void Merge(Type c[],Type d[],int l,int m,int r){int i = l,j = m + 1,k = l;while((i<=m)&&(j<=r)){if(c[i]<=c[j]) d[k++] = c[i++];else d[k++] = c[j++];}if(i>m) {for(int q=j; q<=r; q++) d[k++] = c[q];}else{for(int q=i; q<=m; q++) d[k++] = c[q];}}template <class Type>void MergeSort(Type a[],Type b[],int left,int right){if(left<right){int i = (left + right)/2;MergeSort(a,b,left,i);MergeSort(a,b,i+1,right);Merge(a,b,left,i,right);//合并到数组aCopy(a,b,left,right);//复制回数组a}}3.数据存入文件:本次对文件的输入没有存入文本文件中,只将输出数据输入到指定的文件夹中,用到了输出流文件。
用分治法解决最近点对问题:python实现

⽤分治法解决最近点对问题:python实现 最近点对问题:给定平⾯上n个点,找其中的⼀对点,使得在n个点的所有点对中,该点对的距离最⼩。
需要说明的是理论上最近点对并不⽌⼀对,但是⽆论是寻找全部还是仅寻找其中之⼀,其原理没有区别,仅需略作改造即可。
本⽂提供的算法仅寻找其中⼀对。
解决最近点对问题最简单的⽅法就是穷举法,这样时间复杂度是平⽅级,可以说是最坏的策略。
如果使⽤分治法,其时间复杂度就是线性对数级,这样⼤⼤提⾼了效率。
⾸先⽤分治法解决该问题的基本思路可以参考 /lishuhuakai/article/details/9133961 ,说的很详细,但⼤致思路就是先根据x轴把所有点平分,然后分别在每⼀部分寻找最近点对,最后通过⽐较选⼀个最⼩的。
当然其中最核⼼的地⽅是跨域求距离,原⽂写的很清楚,在此就不再赘述了。
以下是代码:from math import sqrtdef nearest_dot(s):len = s.__len__()left = s[0:len/2]right = s[len/2:]mid_x = (left[-1][0]+right[0][0])/2.0if left.__len__() > 2: lmin = nearest_dot(left) #左侧部分最近点对else: lmin = leftif right.__len__() > 2: rmin = nearest_dot(right) #右侧部分最近点对else: rmin = rightif lmin.__len__() >1: dis_l = get_distance(lmin)else: dis_l = float("inf")if rmin.__len__() >1: dis_2 = get_distance(rmin)else: dis_2 = float("inf")d = min(dis_l, dis_2) #最近点对距离mid_min=[]for i in left:if mid_x-i[0]<=d : #如果左侧部分与中间线的距离<=dfor j in right:if abs(i[0]-j[0])<=d and abs(i[1]-j[1])<=d: #如果右侧部分点在i点的(d,2d)之间if get_distance((i,j))<=d: mid_min.append([i,j]) #ij两点的间距若⼩于d则加⼊队列if mid_min:dic=[]for i in mid_min:dic.append({get_distance(i):i})dic.sort(key=lambda x: x.keys())return (dic[0].values())[0]elif dis_l>dis_2:return rminelse:return lmin# 求点对的距离def get_distance(min):return sqrt((min[0][0]-min[1][0])**2 + (min[0][1]-min[1][1])**2)def divide_conquer(s):s.sort(cmp = lambda x,y : cmp(x[0], y[0])) nearest_dots = nearest_dot(s)print nearest_dots测试⼀下,⽐如说要找这些点中最近的⼀对s=[(0,1),(3,2),(4,3),(5,1),(1,2),(2,1),(6,2),(7,2),(8,3),(4,5),(9,0),(6,4)]运⾏⼀下divide_conquer(s),最终打印出[(6, 2), (7, 2)],Bingo。
最近点对算法

最近点对算法1. 简介最近点对算法(Closest Pair Algorithm)是一种用于找到平面上最近的两个点的算法。
该算法可以在给定一组点的情况下,找到距离最近的两个点,并计算出它们之间的距离。
最近点对问题在计算几何学、图像处理、数据挖掘等领域中具有广泛应用。
例如,在地理信息系统中,可以使用最近点对算法来查找距离最近的两个地理位置;在机器视觉中,可以使用该算法来寻找图像中距离最接近的两个特征点。
2. 算法思想最近点对算法采用分治策略,将问题划分为多个子问题,并通过递归求解子问题来得到整体解。
其基本思想可以概括为以下步骤:1.将所有点按照横坐标进行排序。
2.将排序后的点集平均划分为左右两部分,分别称为P_left和P_right。
3.分别在P_left和P_right中递归求解最近点对。
4.在左右两部分求得的最近点对中,选择距离更小的那一对作为候选解。
5.在区间[P_left[-1].x, P_right[0].x]内,查找可能的更近点对。
6.比较候选解与新找到的更近点对,选择距离更小的那一对作为最终解。
3. 算法实现3.1 数据结构在实现最近点对算法时,需要定义合适的数据结构来表示点。
常见的表示方法是使用二维数组或类对象。
以下是使用类对象来表示点的示例代码:class Point:def __init__(self, x, y):self.x = xself.y = y3.2 算法步骤3.2.1 排序首先,将所有点按照横坐标进行排序。
可以使用快速排序或归并排序等算法来实现排序功能。
def sort_points(points):# 使用快速排序按照横坐标进行排序# ...3.2.2 分治求解将排序后的点集平均划分为左右两部分,并递归求解最近点对。
def closest_pair(points):n = len(points)# 如果点集中只有两个点,则直接返回这两个点和它们之间的距离if n == 2:return points, distance(points[0], points[1])# 如果点集中只有三个点,则直接计算出最近点对if n == 3:d1 = distance(points[0], points[1])d2 = distance(points[0], points[2])d3 = distance(points[1], points[2])if d1 <= d2 and d1 <= d3:return [points[0], points[1]], d1elif d2 <= d1 and d2 <= d3:return [points[0], points[2]], d2else:return [points[1], points[2]], d3# 将点集平均划分为左右两部分mid = n // 2P_left = points[:mid]P_right = points[mid:]# 分别在左右两部分递归求解最近点对closest_pair_left = closest_pair(P_left)closest_pair_right = closest_pair(P_right)# 在左右两部分求得的最近点对中,选择距离更小的那一对作为候选解if closest_pair_left[1] < closest_pair_right[1]:min_pair, min_distance = closest_pair_leftelse:min_pair, min_distance = closest_pair_right3.2.3 查找更近点对在区间[P_left[-1].x, P_right[0].x]内,查找可能的更近点对。
最近点对问题

算法分析与设计最近对问题最近对问题问题描述:在二维平面上的n 个点中,如何快速的找出最近的一对点,就是最近点对问题。
程序设计思想:1.蛮力法求最近对问题:基本思想:分别计算每一对点之间的距离,然后找出距离最小的那一对,为了避免对同一对点计算两次距离,只考虑j i <的那些点对()j i P P ,。
复杂度分析:对于此算法,主要就是算两个点的欧几里得距离。
注意到在求欧几里得距离时,避免了求平方根操作,其原因是:如果被开方的数越小,则它的平方根也越小。
所以复杂度就是求平方,求执行次数为: )()1()(2n O n n n T =-=;即时间复杂度为)(2n O 。
2.分治法求最近对问题:基本思想:用分治法解决最近点对问题,就是将一个问题分解两个子问题,然后递归处理子问题,然后合并。
可能两个点在每个子问题中,也可能两个点分别在两个子问题中,就这两种情况。
则基本过程为:找一条中垂线m (坐位S 集合x 坐标的中位数)把n 个元素分成左右两部分元素,然后分别求得两边的最短距离1d ,2d ,然后取两者中的最小者记为d ,在中线两边分别取d 的距离,记录该距离范围内点的个数,中线左边有L 个元素,右边有R 个元素,分别将两边的点按y 坐标升序排列,在左边集合中每一个点,找右边集合的点,找到与之距离小于d 的点,更新最短距离,直到循环结束,即可求出最短距离。
复杂度分析:应用分治法求解含有n 个点的最近对问题,其时间复杂性可由递推式表示:)()2/(*2)(n f n T n T +=。
由以上分析:合并子问题的解的时间)1()(O n f =。
进而可得分治法求最近对问题的时间复杂度为:)log ()(2n n O n T =。
程序代码:#include <stdio.h>#include <stdlib.h>#include <math.h>#define NUM 1000typedef struct{int x;int y;}N;double distance(N n1,N n2);double minDis(double d1,double d2);double shortestDis(N *data,int length,N *n1 , N *n2); double shortestDis(N *data,int length,N *n1 , N *n2){ int pre,last,middle,median;int i,c1num = 0,c2num = 0,j;N* dataP;N* dataL;N* CP;N* CL;N tn1,tn2;double dis1 ,dis2;// 当只有两个点时,返回最短距离,和点if(length == 2 ){double dis1 = distance(data[0],data[1]);*n1 = data[0];*n2 = data[1];return dis1;}else if(length == 3){// 当只有三个点时,返回最短距离,和点double dis1 = distance(data[0],data[1]);double dis2 = distance(data[1],data[2]);double dis3 = distance(data[0],data[2]);double temp;temp = dis1 < dis2 ? dis1:dis2;temp = temp < dis3 ? temp : dis3;if(temp == dis1){*n1 = data[0];*n2 = data[1];}else if(temp == dis2){*n1 = data[1];*n2 = data[2];}else{*n1 = data[0];*n2 = data[2];}return temp;}middle =length/2;pre = middle;last = length - pre;median = data[middle].x; // 记录中位数dataP = (N*)malloc(sizeof(N)*pre);dataL = (N*)malloc(sizeof(N)*last);CP = (N*)malloc(sizeof(N)*pre);CL = (N*)malloc(sizeof(N)*last);for( i = 0;i < pre ;i++)dataP[i] = data[i];for( i = 0; i< last;i++)dataL[i] = data[i+pre];dis1 = shortestDis(dataP , pre , n1 , n2);dis2 = shortestDis(dataL , last , &tn1 , &tn2);if(dis1 > dis2){*n1 = tn1;*n2 = tn2;}dis1 = minDis(dis1,dis2);for( i = 0; i < pre ; i++)if(dataP[i].x - median < dis1){CP[c1num++] = dataP[i];} // 将在中位数之前的区域中与中位数距离小于最短距离的点放到CP 中for( i = 0; i < last ; i++)if(median - dataL[i].x < dis1){CL[c2num++] = dataL[i];}// 将在中位数之后的区域中与中位数距离小于最短距离的点放到CL 中for(i = 0; i< c1num;i++){for( j =0; j < c2num ; j++){double temp = distance(CP[i],CL[j]);if(temp < dis1){dis1 = temp;*n1 = CP[i];*n2 = CL[j];}}}//依次计算中位数两旁的区域中,每一个点与另外一个区域中的距离,并且记录最短距离return dis1;}double distance(N n1,N n2){return sqrt((n1.x -n2.x)*(n1.x -n2.x) + (n1.y - n2.y)*(n1.y - n2.y));}double minDis(double d1,double d2){double d = d1 < d2 ? d1 : d2;return d;}// 分治法排序void MergeSort(N q[],int num,int mode){int i,nump,numl;N* qPre;N* qLast;if(num == 1 )return;if(num%2&&num != 2){numl = num/2;nump = num/2;nump++;}else{numl = num/2;nump = num/2;}qPre = (N*)malloc(sizeof(N)*nump);qLast = (N*)malloc(sizeof(N)*numl);for(i = 0;i < nump;i++)qPre[i] = q[i];for(i = 0;i<numl;i++)qLast[i] = q[nump+i];MergeSort(qPre,nump,mode);MergeSort(qLast,numl,mode);Merge(qPre,qLast,q,nump,numl,mode);}void Merge(N *pre,N *last,N *total,int nump,int numl,int mode){ int i = 0,j = 0,k = 0;while( i< nump && j< numl ){if(mode == 0){if(pre[i].x > last[j].x ){total[k++] = pre[i++];}else{total[k++] = last[j++];}}else{if(pre[i].y > last[j].y ){total[k++] = pre[i++];}else{total[k++] = last[j++];}}}if(i == nump){for(i = j; i < numl; i++)total[k++] = last[i];}else{for(j = i; j < nump; j++)total[k++] = pre[j];}}void computeShortestDistance(N* data , int num ,int result[4]){FILE *fo;int i,j,l = 0;int *datax,*datay;double dis = 666666,temp;datax = (int*)malloc(sizeof(int)*1000);datay = (int*)malloc(sizeof(int)*1000);for(i =0; i<num ;i++){datax[i] = data[i].x;datay[i] = data[i].y;}for(i = 0;i<num;i++){for(j = i+1;j<num;j++)if((temp = (datax[i] - datax[j])*(datax[i] - datax[j]) + (datay[i] - datay[j])*(datay[i] - datay[j])) < dis){dis = temp;result[0] = datax[i];result[1] = datay[i];result[2] = datax[j];result[3] = datay[j];}}printf("\n蛮力法:\n");printf("shortest dis: %f",sqrt(dis));}void generateDots(int number){FILE *fo;int i,n1,n2;if(!(fo = fopen("data.txt","w"))){printf("open file fail");exit(1);}for(i = 0;i< number;i++){srand((i*i));n1 =rand()%8000;srand(time(NULL)*i*i);n2 = rand()%6000;if(i%2)fprintf(fo,"%d %d\n",n1,n2);elsefprintf(fo,"%d %d\n",n2,n1);}fclose(fo);}int main(){ FILE* fo;N* data;int i;N n1,n2;double dis;int re[4];// 生成数据generateDots(NUM);data = (N*)malloc(sizeof(N)*1000);if(!(fo = fopen("data.txt","r"))){printf("open file fail");exit(1);}for(i = 0;i < NUM;i++){fscanf(fo,"%d %d",&data[i].x,&data[i].y);}fclose(fo);// 合并排序,排好序的数据放置到data 中。
平面最近点对问题(分治)

平⾯最近点对问题(分治)平⾯最近点对问题是指:在给出的同⼀个平⾯内的所有点的坐标,然后找出这些点中最近的两个点的距离.⽅法1:穷举1)算法描述:已知集合S中有n个点,⼀共可以组成n(n-1)/2对点对,蛮⼒法就是对这n(n-1)/2对点对逐对进⾏距离计算,通过循环求得点集中的最近点对2)算法时间复杂度:算法⼀共要执⾏ n(n-1)/2次循环,因此算法复杂度为O(n2)代码实现:利⽤两个for循环可实现所有点的配对,每次配对算出距离然后更新最短距离.for (i=0 ; i < n ;i ++){for(j= i+1 ; j<n ;j ++){点i与点j的配对}}⽅法2:分治1) 把它分成两个或多个更⼩的问题;2) 分别解决每个⼩问题;3) 把各⼩问题的解答组合起来,即可得到原问题的解答。
⼩问题通常与原问题相似,可以递归地使⽤分⽽治之策略来解决。
在这⾥介绍⼀种时间复杂度为O(nlognlogn)的算法。
其实,这⾥⽤到了分治的思想。
将所给平⾯上n个点的集合S分成两个⼦集S1和S2,每个⼦集中约有n/2个点。
然后在每个⼦集中递归地求最接近的点对。
在这⾥,⼀个关键的问题是如何实现分治法中的合并步骤,即由S1和S2的最接近点对,如何求得原集合S中的最接近点对。
如果这两个点分别在S1和S2中,问题就变得复杂了。
为了使问题变得简单,⾸先考虑⼀维的情形。
此时,S中的n个点退化为x轴上的n个实数x1,x2,...,xn。
最接近点对即为这n个实数中相差最⼩的两个实数。
显然可以先将点排好序,然后线性扫描就可以了。
但我们为了便于推⼴到⼆维的情形,尝试⽤分治法解决这个问题。
假设我们⽤m点将S分为S1和S2两个集合,这样⼀来,对于所有的p(S1中的点)和q(S2中的点),有p<q。
递归地在S1和S2上找出其最接近点对{p1,p2}和{q1,q2},并设 d = min{ |p1-p2| , |q1-q2| } 由此易知,S中最接近点对或者是{p1,p2},或者是{q1,q2},或者是某个{q3,p3},如下图所⽰。
平面最近点对问题

平⾯最近点对问题平⾯最近点对问题正如其名,给定平⾯上的n个点,找出其中的⼀对点,使得这对点的距离在所有点对中最⼩。
⾸先显⽽易见地我们可以得到这个问题的O(n^2)算法,枚举所有点对即可。
但是很显然我们可以注意到,这⾥⾯有很多点对显然不是最优的,那么我们可以想到⼀种剪枝⽅法,就是将只对x坐标差值⼩于当前已知最⼩值的点对进⾏判断(否则必然不是最优解),从⽽减少判断量。
我们考虑使⽤分治来实现这种剪枝,先将平⾯上的点分为两部分,分治求出两部分内部的最近点对距离。
之后我们要做的就是枚举两个集合之间的点对,并与两部分内部的最近点对距离⽐较来得到最近点对距离。
这⾥我们是不需要枚举所有点对的,因为我们已经得到了⼀个两部分各⾃内部最⼩的点对距离,因⽽我们可以结合上⾯的根据x坐标的剪枝⽅法,只枚举分别属于两部分的x坐标差⼩于已知最⼩距离的点对。
这样做的复杂度近似于O(n\log^2n),⾄于怎么得到的……我也不知道。
_(:зゝ∠)_例题:1. Vijos 1012 清帝之惑之雍正链接:2. 平⾯最近点对(加强版)链接:另外附上模板:注意,本模板保留六位⼩数,不能直接⽤于提交上⾯的例题,若要提交请修改输出精度。
1 #include <iostream>2 #include <cstdlib>3 #include <cstdio>4 #include <cstring>5 #include <string>6 #include <sstream>7 #include <cctype>8 #include <cmath>9 #include <algorithm>10#define THE_BEST_PONY "Rainbow Dash"1112using namespace std;13const int maxn=220000,INF=~0u>>1;1415struct Point{16double x,y;17bool operator < (const Point &a) const {18if(x<a.x) return true;19if(x>a.x) return false;20return y<a.y;21 }22 }p[maxn];2324int n;25double ans;2627double DisP(int a,int b){28return sqrt((p[a].x-p[b].x)*(p[a].x-p[b].x)+(p[a].y-p[b].y)*(p[a].y-p[b].y));29 }3031double GetAns(int l,int r){32int mid=(l+r)>>1;33if(l==r) return INF;34if(l==r-1) return DisP(l,r);35double len=min(GetAns(l,mid),GetAns(mid+1,r));36for(int i=mid;i>=l;i--){37if(p[i].x+len<p[mid].x) break;38for(int j=mid+1;j<=r;j++){39if(p[mid].x+len<p[j].x) break;40 len=min(len,DisP(i,j));41 }42 }43return len;44 }4546int main(){47 scanf("%d",&n);48for(int i=0;i<n;i++)49 scanf("%lf%lf",&p[i].x,&p[i].y);50 sort(p,p+n);51 ans=GetAns(0,n-1);52 printf("%.6lf",ans);53return0;54 }Processing math: 0%。
最近点对算法

最近点对算法
最近点对算法是一种计算平面上最近的两个点的算法。
该算法是计算机科学中的一个基本问题,也是计算几何学的研究方向之一。
最近点对问题是指在平面上给定n个点,找出其中距离最近的两个点。
该问题的一个简单暴力解法是对每对点进行距离计算,然后选取距离最小的一对。
但该算法的时间复杂度为O(n^2),当n较大时效率较低。
因此,研究者提出了更高效的算法。
其中一种著名的解法是分治法,该算法的时间复杂度为O(nlogn)。
该算法的思路是将点集分成两个子集,然后递归地计算每个子集中的最近点对,并找出跨越两个子集的最近点对。
除分治法外,还有其他更优秀的最近点对算法,如随机增量法、基于网格的算法等。
最近点对算法在计算机科学、地理信息系统、机器人技术等领域有广泛应用。
- 1 -。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
最近点对问题I.一维问题:一、问题描述和分析最近点对问题的提法是:给定平面上n个点,找其中的一对点,使得在n个点组成的所有点对中,该点对间的距离最小。
严格的讲,最接近点对可能多于1对,为简单起见,只找其中的1对作为问题的解。
简单的说,只要将每一点与其它n-1个点的距离算出,找出达到最小距离的2点即可。
但这样效率太低,故想到分治法来解决这个问题。
也就是说,将所给的平面上n个点的集合S 分成2个子集S1和S2,每个子集中约有n/2个点。
然后在每个子集中递归的求其最接近的点对。
这里,关键问题是如何实现分治法中的合并步骤,即由S1和S2的最接近点对,如何求得原集合S中的最接近点对。
如果组成S的最接近点对的2个点都在S1中或都在S2中,则问题很容易解决,但如果这2个点分别在S1和S2中,问题就不那么简单了。
下面的基本算法中,将对其作具体分析。
二、基本算法假设用x轴上某个点m将S划分为2个集合S1和S2,使得S1={x∈S|x<=m};S2={x ∈S|x>m}。
因此,对于所有p∈S1和q∈S2有p<q。
递归的在S1和S2上找出其最接近点对{p1,p2}和{q1,q2},并设d=min{|p1-p2|,|q1-q2|}。
由此易知,S中的最接近点对或者是{p1,p2},或者是{q1,q2},或者是某个{p3,q3},其中p3∈S1且q3∈S2。
如下图所示:S1 S2p1 p2 p3 q1 q2 q3图1 一维情形的分治法注意到,如果S的最接近点对是{p3,q3},即|p3-q3|<d,则p3和q3两者与m的距离不超过d,即|p3-m|<d,|q3-m|<d。
也就是说,p3∈(m-d,m],q3∈(m,m+d]。
由于每个长度为d的半闭区间至多包含S1中的一个点,并且m是S1和S2的分割点,因此(m-d,m]中至少包含一个S中的点。
同理,(m,m+d]中也至少包含一个S中的点。
由上图知,若(m-d,m]中有S的点,则此点就是S1中最大点。
同理,若(m,m+d]中有S的点,则此点就是S2中最小点。
因此,用线性时间就可以找到区间(m-d,m]和(m,m+d]中所有点,即p3和q3。
从而用线性时间就可以将S1的解和S2的解合并成为S的解。
其中,为使S1和S2中有个数大致相等的点,选取S中个点坐标的中位数来作分割点m。
具体算法实现见程序。
三、实现环境本程序在VC++环境下实现。
四、测试情况本程序可按屏幕提示进行输入操作,即相应输出如下:输入一维点集的各元素(以-1结束):5 3 96 8 15 26 -1该一维点集中最近点对为(9,8),其距离为1五、源代码#include "iostream.h"#define M 20struct cpair//表示具有最近距离的点对(d1,d2)的距离dist {float dist;float d1,d2;};int input(float s[],int n)//s[]为一维点集,n为s[]中的元素个数{cout<<"输入一维点集的各元素(以-1结束):\n";n=0;cin>>s[n];while(s[n]!=-1){n++;cin>>s[n];}return n;}float max(float s[],int b,int e)//返回s[b]到s[e]中的最大值{float m1=s[b];for(int i=b+1;i<=e;i++) if(m1<s[i]) m1=s[i];return m1;}float min(float s[],int b,int e)//返回s[b]到s[e]中的最小值{float m1=s[b];for(int i=b+1;i<=e;i++) if(m1>s[i]) m1=s[i];return m1;}//返回s[]中的具有最近距离的点对及其距离cpair cpair1(float s[],int n){cpair temp={1000,0,0};//当点集中元素的个数不足2时,返回具有无穷大的dist值(此处设为1000)的temp if(n<2) return temp;float m1=max(s,0,n-1),m2=min(s,0,n-1);float m=(m1+m2)/2;//找出点集中的中位数int j=0,k=0;//将点集中的各元素按与m的大小关系分组float s1[M],s2[M];for(int i=0;i<n;i++){if(s[i]<=m) {s1[j]=s[i];j++;}else {s2[k]=s[i];k++;}}cpair d1=cpair1(s1,j),d2=cpair1(s2,k);//递归float p=max(s1,0,j-1),q=max(s2,0,k-1);//返回s[]中的具有最近距离的点对及其距离if(d1.dist<d2.dist){if((q-p)<d1.dist){temp.dist=(q-p);temp.d1=q;temp.d2=p;return temp;}else return d1;}else{if((q-p)<d2.dist){temp.dist=(q-p);temp.d1=q;temp.d2=p;return temp;}else return d2;}}void main(){int n,m;float s[M];cpair dist;m=input(s,n);dist=cpair1(s,m);cout<<"\n 该一维点集中最近点对为("<<dist.d1<<","<<dist.d2<<"),其距离为"<<dist.dist<<endl;}II.二维问题一、问题描述与分析问题描述与分析见上。
下面仅对最接近点对中的2个点分别在S1和S2中的情形作具体分析。
二、基本算法S 中的点为平面上的点,它们都有两个坐标值x 和y 。
为了将平面上点集S 线形分割为大小大致相等的两个子集S1和S2,选取一垂直线l :x=m 来作为分割直线。
其中m 为S 中各点x 坐标的中位数。
由此将S 分割为S1={p ∈S|x (p )<=m }和S2={p ∈S|x (p )>m }。
从而使S1和S2分别位于直线l 的左侧和右侧,且S=S 1∪S2。
由于m 是S 中各点x 坐标值的中位数,因此S1和S2中的点数大致相等。
递归的在S1和S2上届最接近点对问题,分别得到S1和S2中的最小距离d1和d2。
现设d=min{d1,d2}。
若S 的最接近点对(p ,q )之间的距离小于d ,则p 和q 必分属于S1和S2。
不妨设p ∈S1,q ∈S2。
p 和q 距离直线l 的距离均小于d 。
因此,若用P1和P2分别表示直线l 的左边和右边的宽为d 的两个垂直长条。
则p ∈P1,q ∈P2,如下图所示。
在二维条件下,P1中所有点与P2中所有点构成的点对均为最接近点对的候选者。
由d 的意义可知,P2中任何两个S 中的点的距离都不小于d 。
由此可推出矩阵R 中最多只有6个S 中的点。
由此稀疏性质,对于P1中任一点p ,P2中最多只有6个点与它构成最接近点对的候选者。
因此,在分治法的合并步骤中,最多只需要检查6*n/2=3*n 个候选者。
但并不确切知道要检查哪6个点。
为解决这问题,可以将p 和P2中所有S2的点投影到垂直线l 上。
由于能与p 点一起构成最接近点对候选者的S2中的点一定在d*2d 的矩形中,所以它们在直线l 上的投影点距p 在l 上投影点的距离小于d 。
由上述分析可知,这种投影点最多有6个。
因此,若将P1和P2中所有S 中点按其y 坐标排好序,则对P1中所有点,对排好序的点列作一次扫描,就可以找出所有最接近点对的候选者。
对P1中每一点最多只要检查P2中排好序的相继6个点。
具体算法见程序。
S1S2图2 距直线l 的距离小于d 的所有点三、实现环境本程序在VC++环境下实现。
四、测试情况本程序可按屏幕提示进行输入操作,即相应输出如下:请输入点集中元素的个数:1该n值不存在最近点对,请重新输入!请输入点集中元素的个数:4请依次输入二维点集中的各点:第1个点:2 5第2个点:1 4第3个点:3 3第4个点:5 8该点集中的最近点对为(1,4)和(2,5),它们的距离为1.41421五、源代码#include "iostream.h"#include "stdio.h"#include "math.h"#define M 20struct point{float x,y;//点的x,y坐标int id;//点在数组中的标号};struct pair{point a,b;float dist;//a,b两点间的距离};//若xx为真时,对point型数组x[n]按元素的x坐标排序;否则按其y坐标排序void sort(point x[],int n,bool xx){for(int i=0;i<n;i++){for(int j=i+1;j<n;j++){if(xx==true)//按元素的x坐标排序{if(x[i].x>x[j].x){//x[i]、x[j]互换point t=x[j];x[j]=x[i];x[i]=t;//修改x[i]、x[j]的id值x[i].id=i;x[j].id=j;}}else//按元素的y坐标排序{if(x[i].y>x[j].y){//x[i]、x[j]互换point t=x[j];x[j]=x[i];x[i]=t;//修改x[i]、x[j]的id值x[i].id=i;x[j].id=j;}}}}}//输入二维点集中的各点,记录于数组x[]中void input(point x[],int n){cout<<"请依次输入二维点集中的各点:\n";for(int i=0;i<n;i++){cout<<"第"<<i+1<<"个点:";cin>>x[i].x>>x[i].y;x[i].id=i;}}float dis(point a,point b){return sqrt((a.x-b.x)*(a.x-b.x)+(a.y-b.y)*(a.y-b.y));}pair closestpair(point x[],point y[],point z[],int l,int r){pair t;if(r==l)//1点的情形{t.dist=1000;return t;}if(r-l==1)//2点的情形{t.a=x[l];t.b=x[r];t.dist=dis(x[l],x[r]);return t;}if(r-l==2)//3点的情形{float d1=dis(x[l],x[l+1]),d2=dis(x[l+1],x[r]),d3=dis(x[l],x[r]);if(d1<=d2 && d1<=d3){t.a=x[l];t.b=x[l+1];t.dist=d1;return t;}if(d2<=d3){t.a=x[l+1];t.b=x[r];t.dist=d2;return t;}else{t.a=x[l];t.b=x[r];t.dist=d3;return t;}}//多于3点的情形,用分治法int m=(l+r)/2;int f=l,g=m+1;for(int i=l;i<=r;i++){if(y[i].id>m) z[g++]=y[i];else z[f++]=y[i];}//递归求解pair best=closestpair(x,z,y,l,m);pair right=closestpair(x,z,y,m+1,r);if(right.dist<best.dist) best=right;//将距中位线l=m的距离小于dist且宽度为2*dist的点置于z[]中int k=l;for(i=l;i<=r;i++){if(abs(x[m].x-y[i].x)<best.dist) z[k++]=y[i];}//搜索z[l:k-1]for(i=l;i<k;i++){for(int j=i+1;j<k && z[j].y-z[i].y<best.dist;j++){float dp=dis(z[i],z[j]);if(dp<best.dist){t.a=z[i];t.b=z[j];t.dist=dp;return t;}}}return best;}//寻找最近点对pair cpair2(point x[],int n){int i;pair t;if(n<2) {t.dist=1000;return t;}//当元素个数不足2时,返回具有较大dist值的t sort(x,n,true);//依x坐标排序point y[M];for(i=0;i<n;i++)//将数组x[]中的点复制到数组y[]中y[i]=x[i];sort(y,n,false);//依y坐标排序point z[M];return closestpair(x,y,z,0,n-1);//计算最近点对}void main(){int n;point x[M];pair t;cout<<"请输入点集中元素的个数:";cin>>n;while(n<2){cout<<"该n值不存在最近点对,请重新输入!\n";cout<<"请输入点集中元素的个数:";cin>>n;}cout<<endl;input(x,n);t=cpair2(x,n);cout<<"\n该点集中的最近点对为("<<t.a.x<<","<<t.a.y<<")和("<<t.b.x<<","<<t.b.y<<"),\n 它们的距离为"<<t.dist<<endl;}。