基于分治的二维平面最近点对算法实现

摘要:

网上有很多关于分治方法求最近点对的讨论,但是没有完整的可运行代码,本文主要对于该问题介绍一完整的可运行代码,

供有兴趣者参考。

正文:

作为对比,我们也同时实现了最近点对的枚举解法,具体的主函数如下:

#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)。

时间: 2024-10-11 05:35:14

基于分治的二维平面最近点对算法实现的相关文章

二维平面最近点-分治

题目描述给出二维平面上的n个点,求其中最近的两个点的距离的一半.输入包含多组数据,每组数据第一行为n,表示点的个数:接下来n行,每行一个点的坐标.当n为0时表示输入结束,每组数据输出一行,为最近的两个点的距离的一半.输入样例:20 01 121 11 13-1.5 00 00 1.50输出样例:0.710.000.75题目解析:采用分治的思想,把n个点按照x坐标进行排序,以坐标mid为界限分成左右两个部分, 对左右两个部分分别求最近点对的距离,然后进行合并.对于两个部分求得的最近距离d, 合并过

HDU 1589 Stars Couple(计算几何求二维平面的最近点对和最远点对)

Time Limit: 1000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 930    Accepted Submission(s): 200 Problem Description Can you believe it? After Gardon had solved the problem, Angel accepted him! They were sitt

数字之魅:寻找二维平面上的最近的点对

在二维平面上的n个点中,如何快速的找出最近的一对点,就是最近点对问题. 初看这个题,可能感觉有点儿复杂. 方案一:蛮力法.数组中总共包含N个数,所以我们可以把平面内所有的点按X轴排序,然后依次算出后一个坐标与前面所有左边的距离,然后用Min和position来记录最近的距离和两个坐标.该方案和在一维空间求两个最近点的距离有点儿类似,其时间复杂度为:O(N*N). 方案二:在一维空间里,我们知道如果数组有序,我们可以很快找出最近的两个点.我们可以用O(N*logN)的时间复杂度来对数据进行排序[快

给定二维平面整数点集输出“最大点集”算法(今日头条面试题)

引子 最近自己的独立游戏上线了,算是了却了一桩心愿.接下来还是想找一份工作继续干,创业的事有缘再说. 找工作之前,总是要浏览一些实战题目,热热身嘛.通过搜索引擎,搜到了今日头条的一道面试题. 题目 P为给定的二维平面整数点集.定义 P 中某点x,如果x满足 P 中任意点都不在 x 的右上方区域内(横纵坐标都大于x),则称其为"最大的".求出所有"最大的"点的集合.(所有点的横坐标和纵坐标都不重复, 坐标轴范围在[0, 1e9) 内) 如下图:实心点为满足条件的点的集

使用PhoneGap开发基于Html5应用二:第一个PhoneGap应用:百度

上一篇博文使用PhoneGap开发基于Html5应用一:PhoneGap简介 中我介绍了如何从phonegap官网上下载源码并启动第一个应用,今天我们把phonegap的应用稍微改一下,让他实现几个比较牛叉的功能: 1.启动一个网页 2.启动摄像头 3.启动定位 老规矩,在实现这几个功能之前我们先讲一下原理性的东西: 首先我们先回顾一下上次说的,其实phonegap应用中有个特别的webview,它就是CDVViewController,关于这个类我们后面再详细描述,现在我们先了解这个类到底做了

Mapreduce执行过程分析(基于Hadoop2.4)——(二)

4.3 Map类 创建Map类和map函数,map函数是org.apache.hadoop.mapreduce.Mapper类中的定义的,当处理每一个键值对的时候,都要调用一次map方法,用户需要覆写此方法.此外还有setup方法和cleanup方法.map方法是当map任务开始运行的时候调用一次,cleanup方法是整个map任务结束的时候运行一次. 4.3.1 Map介绍 Mapper类是一个泛型类,带有4个参数(输入的键,输入的值,输出的键,输出的值).在这里输入的键为Object(默认是

二维平面曼哈顿距离最小生成树模版

#include <stdio.h> #include <iostream> #include <algorithm> #include <sstream> #include <stdlib.h> #include <string.h> #include <limits.h> #include <vector> #include <string> #include <time.h> #i

9.7数学与概率(三)——在二维平面上,有两个正方形,请找出一条直线,能够将这两个正方形对半分

/** * 功能:在二维平面上,有两个正方形,请找出一条直线,能够将这两个正方形对半分. * 假定正方形的上下两条边与x轴平行. */ /** * 考虑: * 线的准确含义,可能性有: * 1)由斜率和y轴截距确定: * 2)由这条边上的任意两点确定: * 3)线段,以正方形的边作为起点和终点. * * 假设:这条线的端点应该落在正方形的边上. * 思路:要将两个正方形对半分,这条线必须连接两个正方形的中心点. */ public class Square { //正方形的四条边 int lef

Ambari系列(四):基于Ambari做二次开发(DIY)

前言 Ambari能够搭建.管理Hadoop集群,这是一件很酷的事情,让集群管理变的easy,但是有可能也有自己的一些需求,如果我想基于Ambari做二次开发DIY,修改一下界面,增加一些功能,添加一个模块等等,这些都是没问题,首先Ambari是基于ASF协议,其次,开发也很方便,唯一不足的是官网基于此的开发文档不多. 我主要介绍下,怎么基于Ambari做二次开发,搭建开发环境,最后编译部署打包. 开发环境搭建 在windows下安装nodejs python的编译运行环境比较复杂,主要是nod