[算法]基于分区最近点算法的二维平面

摘要:

网上有关于寻找最近点的分而治之的方法很多讨论,但是,未完成的可执行代码,本文所介绍的问题一个完整的可执行代码,

参考对于那些有兴趣。

正文:

作为对照。我们也同一时候实现了近期点对的枚举解法,详细的主函数例如以下:

#include<stdio.h>
#include<stdlib.h>
#include "node.h"

void initList(node* p)
{
		p[0].x= 2.0;
		p[0].y= 1.0;

		p[1].x= 1.0;
		p[1].y= 2.0;

		p[2].x= 1.2;
		p[2].y= 3.0;

		p[3].x= 3.0;
		p[3].y= 4.0;

		p[4].x= 5.0;
		p[4].y= 5.0;
	
		p[5].x= 1.5;
		p[5].y= 5.5;

		p[6].x= 2.5;
		p[6].y= 7.0;

		p[7].x= 3.5;
		p[7].y= 8.0;

		p[8].x= 4.0;
		p[8].y= 9.0;

		p[9].x = 3.9;
		p[9].y = 8.8;
}

//測试分治和暴力;
int main()
{
	node* p = (node*)malloc(sizeof(node)*10);
	initList(p);

	double ddf = force(p, 10); //9 is the number of elements;
	printf("the output of force is %lf\n", ddf);

	double dd = callMinDist(p, 10);
	printf("the output of divide-conquer is %lf\n", dd);

	getchar();
	return 0;
}
上述中的force()是枚举的实现,callMinDist则是分治的实现。上述的initList主要对採用的測试案例进行初始化。

以下是node.h头文件的相关代码:

#ifndef __NODE__
#define __NODE__

#define SIZE 4
#define MAX 100000.0

typedef struct{
	double x;
	double y;
}node;


//排序部分;
void mergeX(node a[], node b[], int s, int m, int t);
void mergeY(node a[], node b[], int s, int m, int t);
void mergeSortX(node a[], node b[], int s, int t);
void mergeSortY(node a[], node b[], int s, int t);

//utility;
void show(node* a, int size);
void initList(node* list);
double dist(node* n1, node* n2);

//枚举部分;
double force(node* nodelist, int n);

//分治部分;
double combine(node* py, int n, double lx, double delta);
void copynode(node* dt, node* sr, int b, int n);
double minDist(node* px, node* py, int n);
double callMinDist(node*p, int n);


#endif

枚举部分的代码比較简单。详细看例如以下:

#include <math.h>
#include "node.h"

double dist(node* n1, node* n2)
{
	double temp1 = n1->x - n2->x;
	double temp2 = n1->y - n2->y;

	double sum = temp1*temp1 + temp2*temp2; //pow(temp1, 2)+pow(temp2, 2);
	return sqrt(sum);
}

double force(node* nodelist, int n) //n is number of elements
{
	//这里d须要有一个初始的大值;
	double d = MAX;
	double t;
	
	//函数的主体就是以下这个双层循环;
	for(int i=0; i<n; i++){
		for(int j=i+1; j<n; j++)
		{
			t = dist(&nodelist[i], &nodelist[j]);
			if(t<d)
				d = t;
		}
	}
	//这里最后返回d;
	return d;
}

以下是对于分治算法的调用部分,调用之前须要分别将当中的点按x轴和按y轴进行排序操作,而且

将排完序的点放置在新的存储空间中。

double callMinDist(node* p, int n){

	node* px = (node*)malloc(n*sizeof(node)); //n主要是用于此处的空间申请;
	node* py = (node*)malloc(n*sizeof(node));

	//show(p, n);
	mergeSortX(p, px, 0, n-1); //按点的x轴值排序;
	copynode(px, p, 0, n-1);
	//show(px, n);

	//show(p, n);
	mergeSortY(p, py, 0, n-1); //按点的y轴值排序。
	copynode(py, p, 0, n-1);
	//show(py, n);

	double min = minDist(px, py, n);

	free(px);
	free(py);

	return min;
}

以下就是分治算法的主体:

double minDist(node* px, node* py, int n)  //这里n是number of elements呢?还是max of index?依据以下的空间申请,应该是number of elements.
{
		//printf("n is %d\n", n);
		if(n<=3){
			//show(px, n); //n is number of elements;
			double d = force(px, n); //n is number of elements;
			//printf("n=%d is %lf\n",n, d);
			return d;
		}
		
		int m=n/2;
		double fx = px[m].x;

		node* lx = (node*)malloc(m*sizeof(node));
		node* ly = (node*)malloc(m*sizeof(node));
		node* rx = (node*)malloc((n-m)*sizeof(node));
		node* ry = (node*)malloc((n-m)*sizeof(node));
		
		
		copynode(lx, px, 0, m-1); //对copy而言,这里的m应该是index;
		//show(lx, m);  //show中的n是number of elements;
		//printf("lx :%x\n", lx);

		copynode(rx, px, m, n-1); //copy这边是index;
		//show(rx, n-m);

		copynode(ly, py, 0, m-1);
		//show(ly, m);

		copynode(ry, py, m, n-1);
		//show(ry, n-m);
		
		double d1 = minDist(lx, ly, m); //m is number of elements;
		double dr = minDist(rx, ry, n-m);
		
		double delta = min(d1, dr);
		double d = combine(py, n, fx, delta); //对combine而言,这里的n是number of elements;

		//printf("lx :%x\n", lx);
		free(lx);
		free(ly);
		free(rx);
		free(ry);
		
		return min(delta, d);
}

通过dl = minDist(lx, ly, m)来完毕左半部分的计算;

dr = minDist(rx, ry, n-m)完毕右半部分的计算。

最后通过combine(py, n, fx, delta)将两半部分的结果整合在一起。

这里的关键之处在于combine函数:

double combine(node* py, int n, double lx, double delta)
{
	int num; double d=MAX;
	double tempd;
	node* temp = (node*)malloc(n*sizeof(node));

	int j=0;

	for(int i=0; i<n; i++)
	{
		if(fabs(py[i].x - lx)<= delta){ //求取在区间范围内的点;
			temp[j].x = py[i].x;
			temp[j].y = py[i].y;
			j++;
		}
	}

	num = j;  //temp中的元素

	for(int i=0; i<num; i++)
	{
		for(j=i+1; j<(i+8) && (j<num); j++)
		{
			tempd = dist(&temp[i], &temp[j]);
			if(tempd < d)
				d=tempd;
		}
	}

	free(temp);
	return d;
}
依据书本上的分析。在区间中求取时,仅仅须要计算当前点后(按y轴的值排序)的6到7

个点就可以,因此此处的语句表现为:

for(j=i+1; j<(i+8)&&(j<num); j+)....

最后。我们来看下上述代码中用到的一些周边函数:

void copynode(node* dt, node* sr, int b, int n) //n is max of index;
{
	int k=0;

	for(int i=b; i<=n; i++)
	{
		dt[k].x = sr[i].x;
		dt[k].y = sr[i].y;
		k++;
	}
}

double min(double x, double y)
{
	if(x<=y)
		return x;
	return y;
}

还有是通过归并排序对点集中的点进行排序的过程:

void mergeSortX(node a[], node b[], int s, int t)
{
	if(s == t){
		b[s].x = a[s].x;
		b[s].y = a[s].y;
	}
	else{
		int m = (s+t)/2;
		mergeSortX(a, b, s, m);
		mergeSortX(a, b, m+1, t);
		mergeX(a, b, s, m, t);
	}
}

void mergeSortY(node a[], node b[], int s, int t)
{
	if(s == t){
		b[s].x = a[s].x;
		b[s].y = a[s].y;
	}
	else{
		int m = (s+t)/2;
		mergeSortY(a, b, s, m);
		mergeSortY(a, b, m+1, t);
		mergeY(a, b, s, m, t);
	}
}


void mergeX(node* a, node* b, int s, int m, int t)
{
	int i, j, n;

	for(i=s, j=m+1, n=s; (i<=m)&&(j<=t); n++){
		if(b[i].x<=b[j].x){
			a[n].x = b[i].x;
			a[n].y = b[i].y;
			i++;
		}else{
			a[n].x = b[j].x;
			a[n].y = b[j].y;
			j++;
		}
	}
	while(i<=m){
		a[n].x = b[i].x;
		a[n++].y = b[i++].y;
	}

	while(j<=t){
		a[n].x = b[j].x;
		a[n++].y = b[j++].y;
	}

	//这里须要将a中的数据又一次复制到b中;
	for(int i=s; i<=t; i++){
		b[i].x = a[i].x;
		b[i].y = a[i].y;
	}
}

void mergeY(node* a, node* b, int s, int m, int t)
{
	int i, j, n;

	for(i=s, j=m+1, n=s; (i<=m)&&(j<=t); n++){
		if(b[i].y<=b[j].y){
			a[n].x = b[i].x;
			a[n].y = b[i].y;
			i++;
		}else{
			a[n].x = b[j].x;
			a[n].y = b[j].y;
			j++;
		}
	}
	while(i<=m){
		a[n].x = b[i].x;
		a[n++].y = b[i++].y;
	}

	while(j<=t){
		a[n].x = b[j].x;
		a[n++].y = b[j++].y;
	}

	//这里须要将a中的数据又一次复制到b中;
	for(int i=s; i<=t; i++){
		b[i].x = a[i].x;
		b[i].y = a[i].y;
	}
}
注意:上述的归并排序函数写得不好,不是必需用到a这个參数,全然能够函数内部堆上分配局部变量进行替换。另外代码中反复部分太多,应该能够写得更精简一点。

结论:

针对代码中的简单測试案例。分治案例结果正常;该算法的主要时间复杂度在于排序部分,复杂度为O(nlogn),的复杂性的版本号枚举O(n2)。



版权声明:本文博主原创文章。博客,未经同意不得转载。

posted @ 2015-09-23 15:43  phlsheji  阅读(428)  评论(0编辑  收藏  举报