分治法求最近点对

Dvide and Conquer

Implement the algorithm for the closest pair problem in your favourite language.

INPUT: n points in a plane.

OUTPUT: The pair with the least Euclidean distance.


算法的思想:

首先对所有的点按照x坐标进行从小到大排序,然后利用二分法进行分割,直到区间范围变为2 or 3(即只有2个点或只有3个点),然后可以求这2个点或是3个点的距离(很小的子问题)。

对于每一部分子问题,求其中间mid左右各min范围内的点的距离,如图:

对于这幅图中,只需将绿色的点与右边3个红色的点比较即可(格子按1/2&距离分割,可以保证每个格子内只有1个点)。


对于这幅图,只需将绿色的点与左边6个点比较即可

这里,当我们按照y坐标进行排序后(不考虑x坐标),因此对于每一个点,只需要将其与附近的11个点比较即可。

算法的合并时间:边界附近的点进行排序需要O(nlogn)。

如果每一次迭代保持2个序列,一个按照x进行排序,一个按照y进行排序,像归并排序一样,将预先排序好的2个方向的序列合并起来,实际上只需要0(n)时间。

因此算法的时间复杂度为T(n)=2T(n)+O(n)=0(nlogn)。


input:

-5,-5
-4,-4
-3,-6
-3,-3
-2,-3
-1,-5
0,-3
1,-4
0.5,-3
0.5,-4

output:


#include<iostream>  
#include<string.h>
#include<math.h>
#include <fstream>
#include<iomanip>
#include<algorithm> 
using namespace std;
int k=0; 


struct Record
{
	int c1,c2;
	double ds;
}record[10000];


struct Points
{
	double x,y;
}*points;


double dis(int a,int b)
{
	double dx = points[a].x-points[b].x,dy = points[a].y-points[b].y;
	return sqrt(dx*dx+dy*dy);
}


double min(double a,double b)
{
	return a>b?b:a;
}

   
int cmpx(const Points &a,const Points &b)    
{  
	if(a.x<b.x)   
		return 1;    
	else  
		return 0;   
}  

int cmpy(const Points &a,const Points &b)    
{  
	if(a.y<b.y)   
		return 1;    
	else  
		return 0;   
}  

void storage(double c,int l,int r)
{	//记录最近点对,用于输出
	record[k].c1 = l;
	record[k].c2 = r;
	record[k++].ds = c;
}


double ClosestPair(int l, int r)
{
	if(1==r-l)
	{
		double d = dis(l,r);
		storage(d,l,r);
		return d;
	}
	if(2==r-l)
	{
		double ll = dis(l,l+1);
		double rr = dis(l+1,r);
		storage(ll,l,l+1);
		storage(rr,l+1,r);
		return min(ll,rr);
	}
	int mid = l+((r-l)>>1);
	double dl = ClosestPair(l,mid);
	double dr = ClosestPair(mid+1,r);
	double Min=min(dl,dr);
	int h1=mid,h2=mid;
	while((points[mid].x-points[h1].x<=Min && h1>=l) || (points[h2].x-points[mid].x<=Min && h2<=r))
	{	//得到mid附近2&距离内的区间范围
		h1--;
		h2++;
	}
	sort(&points[l],&points[r],cmpy); //将mid附近2&距离内的点按照y坐标从小到大排序
	for(int i=h1+1;i<h2;i++)
	{
		int k=(i+11)>h2?h2:(i+11);	//只需要比较附近11个点即可
		for(int j=i+1;j<k;j++)
		{
			double d=dis(i,j);
			if(d>=Min)
				break;
			Min = d;
			storage(d,i,j);
		}
	}
	return Min;
}


int main()
{
	ifstream infile("./points.txt", ios::in); 
    char buf[30];
	double (*data)[2];	
	char *subarr=NULL;
	char *delims = ",";
	int n;
	cout<<"Please input the number of points(eg.input 10 int this test):"<<endl;
	cin>>n;
	points = new struct Points[n];
	data = new double[n][2];
    if(!infile.is_open())
	{
		cout<<"Error opening file!";
		exit(1);
	}
	int i=0,j=0,p=0;
	while(infile.getline(buf,30)!=NULL)
	{
		j = 0;
		subarr = strtok(buf,delims);
		while(subarr!=NULL)
		{
			data[i][j] = atof(subarr);	
			subarr = strtok(NULL,delims);
			j++;
		}
		
		points[p].x = data[i][0];
		points[p++].y = data[i][1];
		i++;
		
	}
	infile.close();
	sort(&points[0],&points[n],cmpx); //按横坐标从小到大排序
	double value = ClosestPair(0,n-1);
	int x,y;
	for(i=0;i<k;i++)
	{
		if(record[i].ds==value)
		{
			
			x = record[i].c1;
			y = record[i].c2;
			cout<<"points("<<points[x].x<<","<<points[x].y<<")and("<<points[y].x<<","<<points[y].y<<")"<<endl;
		}
	}
	cout<<"have the shortest distance :"<<value<<endl;
    return 0;
}


以下是C语言分治法最近对问题的代码: #include <stdio.h> #include <stdlib.h> #include <math.h> #define INF 1e20 //定义无穷大 //定义的结构体 typedef struct { double x; double y; } Point; //比较函数,按x坐标排序 int cmp_x(const void *a, const void *b) { Point *p1 = (Point *)a; Point *p2 = (Point *)b; if (p1->x < p2->x) return -1; else if (p1->x > p2->x) return 1; else return 0; } //比较函数,按y坐标排序 int cmp_y(const void *a, const void *b) { Point *p1 = (Point *)a; Point *p2 = (Point *)b; if (p1->y < p2->y) return -1; else if (p1->y > p2->y) return 1; else return 0; } //计算两之间的距离 double dist(Point p1, Point p2) { return sqrt((p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y)); } //暴力最近对 double brute_force(Point *p, int n) { double min_dist = INF; for (int i = 0; i < n-1; i++) { for (int j = i+1; j < n; j++) { double d = dist(p[i], p[j]); if (d < min_dist) min_dist = d; } } return min_dist; } //合并最近对 double merge(Point *p, int n, double d) { double mid = p[n/2].x; //中线 Point *strip = (Point *)malloc(n * sizeof(Point)); int j = 0; for (int i = 0; i < n; i++) { if (fabs(p[i].x - mid) < d) strip[j++] = p[i]; } qsort(strip, j, sizeof(Point), cmp_y); //按y坐标排序 double min_dist = d; for (int i = 0; i < j-1; i++) { for (int k = i+1; k < j && strip[k].y-strip[i].y < min_dist; k++) { double d = dist(strip[i], strip[k]); if (d < min_dist) min_dist = d; } } free(strip); return min_dist; } //分治最近对 double closest_pair(Point *p, int n) { if (n <= 3) return brute_force(p, n); int mid = n/2; double dl = closest_pair(p, mid); //左半边最近对距离 double dr = closest_pair(p+mid, n-mid); //右半边最近对距离 double d = fmin(dl, dr); //取左右半边最近对距离的较小值 return merge(p, n, d); //合并最近对 } int main() { int n; printf("请输入的数量:"); scanf("%d", &n); Point *p = (Point *)malloc(n * sizeof(Point)); printf("请输入%d个的坐标:\n", n); for (int i = 0; i < n; i++) { scanf("%lf%lf", &p[i].x, &p[i].y); } qsort(p, n, sizeof(Point), cmp_x); //按x坐标排序 double ans = closest_pair(p, n); printf("最近对的距离为:%lf\n", ans); free(p); return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值