0007算法笔记——【分治法】最接近点对问题

合集下载

最接近点对问题教材

最接近点对问题教材

S2中的q3组成,则p3和q3一定在划分平面 L的距离d内。
第二步筛选:考虑P1中任意一点p,它若与
P2中的点q构成最接近点对的候选者,则必 有distance(p,q)<d。满足这个条件的P2 中的点定落在一个d×2d×2d的长方体R中
重要观察结论:P2中任何2个S中的点的距离 都不小于d。由此可以推出长方体R中最多只有 24个S中的点。
分治法解决二维空间最接近点问题
选取一垂直线l:x=m来作为分割直线。其中m为S中各 点x坐标的中位数。由此将S分割为S1和S2。 递归地在S1和S2上找出其最小距离d1和d2,并设 d=min{d1,d2},S中的最接近点对或者是d,或者是某个 {p,q},其中p∈S1且q∈S2。
第一步筛选:如果最近点对由S1中的p3和
4、设P1是S1中距垂直分割线l的距离在dm之 内的所有点组成的集合; { P2是S2中距分割线l的距离在dm之内所有 n=|S|; 点组成的集合; O(n) 将 P1 和 P2 中点依其 y 坐标值排序; if (n < 2) return ; 并设X和Y是相应的已排好序的点列; 1、m=S中各点x间坐标的中位数; 5、通过扫描X以及对于X中每个点检查Y中与 构造S1和S2; 其距离在dm之内的所有点(最多6个)可以完成 O(n) 合并; O(n) //S1={p∈S|x(p)<=m}, 当X中的扫描指针逐次向上移动时,Y中的 S2={p∈S|x(p)>m} 扫描指针可在宽为2dm的区间内移动; 2、d1=cpair2(S1); 设dl是按这种扫描方式找到的点对间的最 2T(n/2 小距离; d2=cpair2(S2); ) 6、d=min(dm,dl); 常数时间 3、dm=min(d1,d2); return d; 常数时间 }

最接近点对问题

最接近点对问题

一、最接近点对问题(一维)一、最接近点对问题(一维)最接近点对问题:给定平面上n个点,找其中的一对点,使得在n个点的所有点对中,该点对的距离最小。

此时S中的n个点退化为x轴上的n个实数x1,x2,..,x n。

最接近点对即为这n个实数中相差最小的2个实数。

2、分析将所给的平面上n个点的集合S分成2个子集S1和S2,每一个子集中约有n/2个点,·然后在每一个子集中递归地求其最接近的点对。

S1和S2的最接近点对未必就是S的最接近点对,若是组成S的最接近点对的2个点都在S1中或都在S2中,则问题很容易解决。

可是,若是这2个点别离在S1和S2中,则对于S1中任一点p,S2中最多只有n/2个点与它组成最接近点对的候选者,仍需做n2/4次计算和比较才能肯定S的最接近点对。

因此,依此思路,归并步骤耗时为O(n2)。

整个算法所需计算时间T(n)应知足:T(n)=2T(n/2)+O(n2)它的解为T(n)=O(n2)3、伪代码随机Randomfloat Random(){float result=rand()%10000;return result*;}返回最大、最小float Max OR Min(float s[],int p,intq)序实现#include<>#include<iostream>#include<cmath>using namespace std;const int M=50;D=i;X[i].x=Random();X[i].y=Random();c out<<"("<<X[i].x<<","<<X[i].y<<") ";}PointX a;PointX b;float d;Cpair2(X,number_used,a,b,d);cout<<endl;cout<<"the closest pair is ("<<<<","<<<<")和("<<<<","<<<<") "<<endl;cout<<"the min distance is "<<d<<endl;return 0;}float Random(){float result=rand()%10000;return result*;}=i;Y[i].x=X[i].x;Y[i].y=X[i].y;}PointY*tmpy=new PointY[n];MergeSort(Y,tmpy,0,n-1);PointY*Z=new PointY[n];closest(X,Y,Z,0,n-1,a,b,d);delete []Y;delete []Z;return true;}void closest(PointX X[],PointY Y[],PointY Z[], int l, int r,PointX& a,PointX& b,float& d){i f(r-l==1){>m) Z[g++]=Y[i];else Z[f++]=Y[i]; closest(X,Z,Y,l,m,a,b,d);float dr;PointX ar,br;closest(X,Z,Y,m+1,r,ar,br,dr);if(dr<d){a=ar;b=br;d=dr;}Merge(Z,Y,l,m,r);-Y[t].x)<d){Z[k++]=Y[t];}-Z[s].y<d;j++){float dp=dis(Z[s],Z[j]);if(dp<d){d=dp;a=X[Z[i].p];b=X[Z[j].p];} } } }template <typename Type>void Merge(Type c[],Type d[], int l, int m, int r){i nt i=l,j=m+1,k=l;w hile((i<=m)&&(j<=r))if(c[i]<=c[j])d[k++]=c[i++];elsed[k++]=c[j++];if(i>m)for(int q=j;q<=r;q++)d[k++]=c[q];elsefor(int q=i;q<=m;q++)d[k++]=c[q];}template <typename Type>void MergeSort(Type a[], Type b[],int left, int right){ // a[]为待排序的数组,left,right为a数组的下标,b[]为临时存放排好序的临时数组i f(left<right){int i=(left+right)/2;//取中点MergeSort(a,b,left,i);MergeSort(a,b,i+1,right);Merge(a,b,left,i,right);//归并到数组bCopy(a,b,left,right);//复制回数组a }}template <typename Type>void Copy(Type a[],Type b[], int right,int left) {for(int i=right;i<=left;i++)a[i]=b[i];}运行情况:。

1007分治法(最近点对)

1007分治法(最近点对)

由此可得:
C11 C12 C 21 C 22
A11 B11 A12 B21 A11 B12 A12 B22 A21 B11 A22 B21 A21 B12 A22 B22
Strassen矩阵乘法
(1 ) C12 A11 AO B11 B12 n 2 C11 T ( 12 n) 2 C 7 T ( n / 2 ) O ( n ) n2 C A A B B 22 22 21 22 21 21 M 1 A11 ( B12 B22 ) T(n)=O(nlog7) =O(n2.81)较大的改进 C11 M 5 M 4 M 2 M 6 M 2 ( A11 A12 ) B22
规模较小的情况
n=2
规模较小的情况
n=4
规模较小的情况
n=4
规模较小的情况
n=8
规模较小的情况
n=8
规模较大的情况
当k>0时,将2k×2k棋盘分割为4个2k-1×2k-1 子棋盘(a)所示。 特殊方格必位于4个较小子棋盘之一中,其余3个子棋盘中无特 殊方格。为了将这3个无特殊方格的子棋盘转化为特殊棋盘, 可以用一个L型骨牌覆盖这3个较小棋盘的会合处,如 (b)所 示,从而将原问题转化为4个较小规模的棋盘覆盖问题。递归 地使用这种分割,直至棋盘简化为棋盘1×1。
Strassen矩阵乘法
传统方法:O(n3)
A和B的乘积矩阵C中的元素C[i,j]定义为:C[i][ j ] A[i][ k ]B[k ][ j ]
k 1 n
若依此定义来计算A和B的乘积矩阵C,则每计 算C的一个元素C[i][j],需要做n次乘法和n-1次 加法。因此,算出矩阵C的 个元素所需的计算 时间为O(n3)

最近点对问题

最近点对问题

最近点对问题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中的点。

平面最接近点对问题(分治法)

平面最接近点对问题(分治法)

平⾯最接近点对问题(分治法)头⽂件部分:(1)数据结构部分://涉及的数据结构#ifndef DATASET_H#define DATASET_Hstruct point{ //点结构double x, y;};#endif(2)函数声明部分://函数头⽂件//=======================================#ifndef FUNC_H#define FUNC_H#include "dataset.h"double closest_distance(point s[], int low, int high, point rec[]);double Distance(point a, point b);bool comp_x(point a, point b);bool comp_y(point a, point b);#endif源⽂件部分:(1)函数实现部分://求解最近距离的函数实现#include "dataset.h"#include <math.h>#include <algorithm>using namespace std;double Distance(point a, point b) //计算两点距离{return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) - (a.y - b.y));}bool comp_x(point a, point b) //按x升序判别函数{return a.x < b.x;}bool comp_y(point a, point b) //按y升序判别函数{return a.y < b.y;}//函数实现:rec[]存储最接近点对;double closest_distance(point s[], int low, int high, point rec[]){double d1, d2, d3, d;int mid, i, j, index;double x1, y1, x2, y2; //记录最近的点对point *P = new point[high - low + 1];point temp_1[2], temp_2[2], temp_3[2];if (high - low == 1) //两个点时的情况{rec[0].x = s[low].x; rec[0].y = s[low].y;rec[1].x = s[high].x; rec[1].y = s[high].y;return Distance(s[low], s[high]);}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 <= d3)) //这⾥在判断三种情况时,第⼆种情况没必要使⽤(d2<d3)&&(d2<d1),相较更繁琐了;{rec[0].x = s[low].x; rec[0].y = s[low].y;rec[1].x = s[low + 1].x; rec[1].y = s[low + 1].y;return d1;}else if (d2 < d3){rec[0].x = s[low + 1].x; rec[0].y = s[low + 1].y;rec[1].x = s[high].x; rec[1].y = s[high].y;return d2;}else{rec[0].x = s[low].x; rec[0].y = s[low].y;rec[1].x = s[high].x; rec[1].y = s[high].y;return d3;}}mid = (low + high) / 2;d1 = closest_distance(s, low, mid, rec);temp_1[0] = rec[0];temp_1[1] = rec[1];d2 = closest_distance(s, mid + 1 ,high, rec);temp_2[0] = rec[0];temp_2[1] = rec[1];if (d1 <= d2){d = d1;rec[0] = temp_1[0];rec[1] = temp_1[1];}else{d = d2;rec[0] = temp_2[0];rec[1] = temp_2[1];}index = 0;for (i = mid; (i >= low) && ((s[mid].x - s[i].x) < d); i--){P[index++] = s[i]; //点集合P1}for (i = mid + 1; (i <= high) && ((s[i].x - s[mid].x) < d); i++){P[index++] = s[i]; //点集合P2}sort(P, P + index, comp_y); //升序排列for (i = 0; i < index; i++){for (j = i + 1; j < index; j++){if ((P[j].y - P[i].y) >= d)break;else{d3 = Distance(P[i], P[j]);if (d3 < d){rec[0].x = P[i].x; rec[0].y = P[i].y;rec[1].x = P[j].x; rec[1].y = P[j].y;d = d3;}}}}delete []P; //注意动态内存的删除⽅式,防⽌内存泄漏return d;}(2)主函数部分://2018_02_24//使⽤分治法求解⼆维平⾯最接近点问题//============================================================= #include <iostream>#include <math.h>#include <algorithm>#include "dataset.h"//数据结构实现放在这个头⽂件中#include "func.h"//函数声明的头⽂件using namespace std;int main(void){point p[10]; //设定点的集合int n;double minDist;cout << "输⼊点的个数:\n";cin >> n;cout << "输⼊点集:(x , y) \n";for (int i = 0; i < n; i++)cin >> p[i].x >> p[i].y;sort(p, p + n, comp_x); //对输⼊的点先进⾏排序point index[2];minDist = closest_distance(p, 0, n - 1, index);cout << "最⼩距离点对为:(" << index[0].x << "," << index[0].y << "),(" << index[1].x << "," << index[1].y << ")"; cout << "最⼩距离为:\n" << minDist;system("pause");return0;}最后,结果如下:。

分治算法知识点总结

分治算法知识点总结

分治算法知识点总结一、基本概念分治算法是一种递归的算法,其基本思想就是将原问题分解成多个相互独立的子问题,然后分别解决这些子问题,最后将子问题的解合并得到原问题的解。

分治算法的核心思想可以用一句话概括:分而治之,分即是将原问题分解成若干个规模较小的子问题,治即是解决这些子问题,然后将子问题的解合并起来得到原问题的解。

分治算法通常包括三个步骤:(1)分解:将原问题分解成若干个规模较小的子问题;(2)解决:递归地解决这些子问题;(3)合并:将子问题的解合并起来得到原问题的解。

分治算法的典型特征包括递归和合并。

递归指的是将原问题分解成若干个规模较小的子问题,然后递归地解决这些子问题;合并指的是将子问题的解合并得到原问题的解。

通常来说,分治算法的递归实现方式很容易编写,但有时可能会面临大量的重复计算,因此需要合并操作来避免这种情况。

二、原理分治算法的原理可以通过一个简单的例子来说明。

我们以计算数组中的最大值为例,具体的步骤如下:(1)分解:将数组分解成两个规模相等的子数组;(2)解决:递归地在这两个子数组中分别找到最大值;(3)合并:比较这两个子数组的最大值,得到原数组的最大值。

从这个例子可以看出,分治算法将原问题分解成两个子问题:分别在左边子数组和右边子数组中找到最大值,然后将这两个子问题的解合并起来得到原数组的最大值。

这种将问题分解成若干个规模较小的子问题,然后合并子问题的解得到原问题的解的方法正是分治算法的核心原理。

分治算法的优势在于它可以将原问题分解成多个规模较小的子问题,然后并行地解决这些子问题,最后合并子问题的解得到原问题的解。

这种并行的设计思路使得分治算法非常适合于并行计算,能够有效地提高计算效率。

三、应用分治算法在计算机科学领域有着广泛的应用,包括排序、搜索、图论、动态规划等多个方面。

下面我们将以排序算法和搜索算法为例,来介绍分治算法在实际应用中的具体情况。

1. 排序算法排序算法是计算机科学领域中一个重要的问题,分治算法在排序算法中有着广泛的应用。

用分治法解决最近点对问题:python实现

用分治法解决最近点对问题: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.蛮力法求最近对问题:1)基本思想:分别计算每一对点之间的距离,然后找出距离最小的那一对,为了避免对同一对点计算两次距离,只考虑ji<的那些点对()j i P P,.2)复杂度分析:对于此算法,主要就是算两个点的欧几里得距离。

注意到在求欧几里得距离时,避免了求平方根操作,其原因是:如果被开方的数越小,则它的平方根也越小。

所以复杂度就是求平方,求执行次数为: nnT==;即时间复杂度为)-n)1)O(((2n)O。

(2n2.分治法求最近对问题:1)基本思想:用分治法解决最近点对问题,就是将一个问题分解两个子问题,然后递归处理子问题,然后合并。

可能两个点在每个子问题中,也可能两个点分别在两个子问题中,就这两种情况。

则基本过程为:找一条中垂线m(坐位S集合x坐标的中位数)把n个元素分成左右两部分元素,然后分别求得两边的最短距离1d ,2d ,然后取两者中的最小者记为d ,在中线两边分别取d 的距离,记录该距离范围内点的个数,中线左边有L 个元素,右边有R 个元素,分别将两边的点按y 坐标升序排列,在左边集合中每一个点,找右边集合的点,找到与之距离小于d的点,更新最短距离,直到循环结束,即可求出最短距离.2)复杂度分析:应用分治法求解含有n 个点的最近对问题,其时间复杂性可由递推式表示:)()2/(*2)(n f n T n T +=。

由以上分析:合并子问题的解的时间)1()(O n f =。

进而可得分治法求最近对问题的时间复杂度为:)log ()(2n n O n T =.三、源程序及注释:#include<iostream>#include 〈cstring 〉#include 〈cmath>#include 〈algorithm>#include<time.h>using namespace std ;#define eps 1e-8#define MAXN 10000000#define N 5000struct Point{double x,y;};Point S[N*2],S1[N],S2[N],P1[N],P2[N];double Distance(Point a,Point b){return sqrt((a。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

问题场景:在应用中,常用诸如点、圆等简单的几何对象代表现实世界中的实体。

在涉及这些几何对象的问题中,常需要了解其邻域中其他几何对象的信息。

例如,在空中交通控制问题中,若将飞机作为空间中移动的一个点来看待,则具有最大碰撞危险的2架飞机,就是这个空间中最接近的一对点。

这类问题是计算几何学中研究的基本问题之一。

问题描述:给定平面上n个点,找其中的一对点,使得在n个点的所有点对中,该点对的距离最小。

严格地说,最接近点对可能多于1对。

为了简单起见,这里只限于找其中的一对。

1、一维最接近点对问题算法思路:这个问题很容易理解,似乎也不难解决。

我们只要将每一点与其他n-1个点的距离算出,找出达到最小距离的两个点即可。

然而,这样做效率太低,需要O(n^2)的计算时间。

在问题的计算复杂性中我们可以看到,该问题的计算时间下界为Ω(nlogn)。

这个下界引导我们去找问题的一个θ(nlogn)算法。

采用分治法思想,考虑将所给的n个点的集合S 分成2个子集S1和S2,每个子集中约有n/2个点,然后在每个子集中递归地求其最接近的点对。

在这里,一个关键的问题是如何实现分治法中的合并步骤,即由S1和S2的最接近点对,如何求得原集合S中的最接近点对,因为S1和S2的最接近点对未必就是S的最接近点对。

如果组成S的最接近点对的2个点都在S1中或都在S2中,则问题很容易解决。

但是,如果这2个点分别在S1和S2中,则对于S1中任一点p,S2中最多只有n/2个点与它构成最接近点对的候选者,仍需做n^2/4次计算和比较才能确定S的最接近点对。

因此,依此思路,合并步骤耗时为O(n^2)。

整个算法所需计算时间T(n)应满足:T(n)=2T(n/2)+O(n^2)。

它的解为T(n)=O(n^2),即与合并步骤的耗时同阶,这不比用穷举的方法好。

从解递归方程的套用公式法,我们看到问题出在合并步骤耗时太多。

这启发我们把注意力放在合并步骤上。

设S中的n个点为x轴上的n个实数x1,x2,..,xn。

最接近点对即为这n个实数中相差最小的2个实数。

我们显然可以先将x1,x2,..,x n排好序,然后,用一次线性扫描就可以找出最接近点对。

这种方法主要计算时间花在排序上,在排序算法已经证明,时间复杂度为O(nlogn)。

然而这种方法无法直接推广到二维的情形。

因此,对这种一维的简单情形,我们还是尝试用分治法来求解,并希望能推广到二维的情形。

假设我们用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。

如图所示。

如果S的最接近点对是{p3,q3},即|p3-q3|<d,则p3和q3两者与m的距离不超过d,即|p3-m|<d,|q3-m|<d,也就是说,p3∈(m-d,m],q3∈(m,m+d]。

由于在S1中,每个长度为d的半闭区间至多包含一个点(否则必有两点距离小于d),并且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的解。

也就是说,按这种分治策略,合并步可在O(n)时间内完成。

这样是否就可以得到一个有效的算法了呢?还有一个问题需要认真考虑,即分割点m的选取,及S1和S2的划分。

选取分割点m的一个基本要求是由此导出集合S的一个线性分割,即S=S1∪S2,S1∩S2=Φ,且S1={x|x≤m};S2={x|x>m}。

容易看出,如果选取m=[max(S)+min(S)]/2,可以满足线性分割的要求。

选取分割点后,再用O(n)时间即可将S划分成S1={x∈S|x≤m}和S2={x∈S|x>m}。

然而,这样选取分割点m,有可能造成划分出的子集S1和S2的不平衡。

例如在最坏情况下,|S1|=1,|S2|=n-1,由此产生的分治法在最坏情况下所需的计算时间T(n)应满足递归方程:T(n)=T(n-1)+O(n)它的解是T(n)=O(n^2)。

这种效率降低的现象可以通过分治法中“平衡子问题”的方法加以解决。

即通过适当选择分割点m,使S1和S2中有大致相等个数的点。

自然地,我们会想到用S的n个点的坐标的中位数来作分割点。

在选择算法中介绍的选取中位数的线性时间算法使我们可以在O(n)时间内确定一个平衡的分割点m。

本程序确定平衡点采用m=[max(S)+min(S)]/2方法。

如果需要利用中位数作分割点,看结合笔者博文《0005算法笔记——线性时间选择》改写。

一维最接近临近点对问题程序清单如下:[cpp]view plain copy1.//2d10-1 一维最邻近点对问题2.#include "stdafx.h"3.#include <ctime>4.#include <iostream>ing namespace std;6.7.const int L=100;8.//点对结构体9.struct Pair10.{11.float d;//点对距离12.float d1,d2;//点对坐标13.};14.float Random();15.int input(float s[]);//构造S16.float Max(float s[],int p,int q);17.float Min(float s[],int p,int q);18.template <class Type>19.void Swap(Type &x,Type &y);20.template <class Type>21.int Partition(Type s[],Type x,int l,int r);22.Pair Cpair(float s[],int l,int r);23.24.int main()25.{26. srand((unsigned)time(NULL));27.int m;28.float s[L];29. Pair d;30. m=input(s);31. d=Cpair(s,0,m-1);32. cout<<endl<<"最近点对坐标为: (d1:"<<d.d1<<",d2:"<<d.d2<<")";33. cout<<endl<<"这两点距离为: "<<d.d<<endl;34.return 0;35.}36.37.38.float Random()39.{40.float result=rand()%10000;41.return result*0.01;42.}43.44.int input(float s[])45.{46.int length;47. cout<<"输入点的数目: ";48. cin>>length;49. cout<<"点集在X轴上坐标为:";50.for(int i=0;i<length;i++)51. {52. s[i]=Random();53. cout<<s[i]<<" ";54. }55.56.return length;57.}58.59.60.float Max(float s[],int l,int r)//返回s[]中的最大值61.{62.float s_max=s[l];63.for(int i=l+1;i<=r;i++)64.if(s_max<s[i])65. s_max=s[i];66.return s_max;67.}68.69.float Min(float s[],int l,int r)//返回s[]中的最小值70.{71.float s_min=s[l];72.for(int i=l+1;i<=r;i++)73.if(s_min>s[i])74. s_min=s[i];75.return s_min;76.}77.78.template <class Type>79.void Swap(Type &x,Type &y)80.{81. Type temp = x;82. x = y;83. y = temp;84.}85.86.template <class Type>87.int Partition(Type s[],Type x,int l,int r)88.{89.int i = l - 1,j = r + 1;90.91.while(true)92. {93.while(s[++i]<x && i<r);94.while(s[--j]>x);95.if(i>=j)96. {97.break;98. }99. Swap(s[i],s[j]);100. }101.return j;102.}103.104.//返回s[]中的具有最近距离的点对及其距离105.Pair Cpair(float s[],int l,int r)106.{107. Pair min_d={99999,0,0};//最短距离108.109.if(r-l<1) return min_d;110.float m1=Max(s,l,r),m2=Min(s,l,r);111.112.float m=(m1+m2)/2;//找出点集中的中位数113.114.//将点集中的各元素按与m的大小关系分组115.int j = Partition(s,m,l,r);116.117. Pair d1=Cpair(s,l,j),d2=Cpair(s,j+1,r);//递归118.float p=Max(s,l,j),q=Min(s,j+1,r);119.120.//返回s[]中的具有最近距离的点对及其距离121.if(d1.d<d2.d)122. {123.if((q-p)<d1.d)124. {125. min_d.d=(q-p);126. min_d.d1=q;127. min_d.d2=p;128.return min_d;129. }130.else return d1;131. }132.else133. {134.if((q-p)<d2.d)135. {136. min_d.d=(q-p);137. min_d.d1=q;138. min_d.d2=p;139.return min_d;140. }141.else return d2;142. }143.}程序运行结果如下:该算法的分割步骤和合并步骤总共耗时O(n)。

相关文档
最新文档