1. 程式人生 > >最鄰近點問題----分治法

最鄰近點問題----分治法

分治法

劃分問題:把問題的例項劃分成子問題

遞迴求解:遞迴解決子問題

合併求解:合併子問題的解得到原問題的解

“關鍵”步驟在與合併。

最鄰近點問題

問題描述:

輸入:空間平面上點集Q   輸出:距離最近的兩個點對

以下分為一維、二維、和三維使用分治法來分析最鄰近點問題。

一維:如果是在一個直線上找最近的點對,則可以使用排序,之後找最近最近點。

分治思路:

Divide 將其劃分成兩個部分,Q1,Q2, T(n) = O(n)

Conquer 分別找最近點對<p1,p2>,<q1,q2> T(n) = 2T(n/2)

Merge 比較分開點附近的兩個點距離<p2,q3>和找出的<p1,p2> <q1,q2>的距離T(n) = O(n)

bubuko.com,布布扣

時間複雜度:T(n) = O(1)   n=2

T(n) = 2T(n/2) + O(n) = O(nlogn) n >= 3

二維空間問題:

bubuko.com,布布扣

Divide階段和Conquer階段都和一維問題類似,使用x=m的直線劃分為兩個子部分,可以找出最近點對<p1,p2>,<q1,q2>

令d=min{<p1,p2>,<q1,q2>},則merge階段的點一定在[m-d,m+d]範圍內。

似乎是優化很多,但是最糟糕的情況是在改範圍內的點很多則對應起來還是會有n^2級複雜度。

bubuko.com,布布扣

如圖,在L:x=m左側區域中的點P(x0,y0),則距離其小於d的點在以P的圓心,半徑為d的圓內,


則在L:x=m右側的部分圓一定在以(m,y0-d)的左下角頂點,(m+d,y0+d)為右上角頂點的矩形內,即圖中紅色矩形區域內。

bubuko.com,布布扣

在對該矩形進行分6塊,長為2d/3,寬為d/2,則每個小矩形的內的點最長距離為5d/6(對角線),即每個小矩形最多有一個點。

因為如果有兩個點在同一個矩形則其最小距離不會是d。

因此對[m-d,m+d]範圍內每個頂點最多有6個點需要判斷。

需要注意的事項:

1.      要設定好資料結構便於處理

2.      要進行預處理,對點集分別按x,y排序,維護兩個排序排序結合。

時間複雜度:T(n)=O(1)    n=2

T(n)=O(n)+2T(n/2)+O(n)=O(nlogn)  n≥3


程式碼如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

//定義點和兩個陣列
struct POINT 
{
	double x,y;
}point[10000],temp[10000];

//兩點之間距離
double dis(struct POINT p1, struct POINT p2)
{
	return sqrt((p1.x - p2.x) * (p1.x - p2.x) +
				(p1.y - p2.y) * (p1.y - p2.y) );
}

//排序判斷函式,按x座標排序
int cmp(const void * a, const void * b)
{
	struct POINT * c = (struct POINT *)a;
	struct POINT * d = (struct POINT *)b;
	if (c->x != d->x)
	{
		return c->x > d->x;
	}
	else 
		return c->y > d->y;
}

//排序判斷函式,按y座標排序
int cmp1(const void * a, const void * b)
{
	struct POINT * c = (struct  POINT *)a;
	struct POINT * d = (struct  POINT *)b;
	if (c->y != d->y)
	{
		return c->y > d->y;
	}
	else 
		return c->x > d->x;
}

double findMin(int l, int r)
{
	if (l == r)
	{
		return 10010;
	}
	if (l == r - 1)
	{
		return dis(point[l], point[r]);
	}
	double tmp1 = findMin(l,(l + r) >> 1);
	double tmp2 = findMin(((l + r) >> 1) + 1, r);
	double Mindis,tmp, mid;
	mid = point[(l + r) >> 1].x;
	/*mid = (point[l].x + point[r].x) / 2.0;*/
	int i,j,cnt = 0;
	if (tmp1 < tmp2)
	{
		Mindis = tmp1;
	}
	else
		Mindis = tmp2;
	for (i = l; i <= r; ++ i)
	{
		if (fabs(point[i].x - mid) < Mindis)
		{
			temp[cnt ++] = point[i];
		}
	}
	qsort(temp, cnt, sizeof(temp[0]), cmp1);
	for (i = 0; i < cnt - 1; ++ i)
	{
		/*for (j = i + 1; j < cnt; ++ j)*/
		for (j = i + 1; j < i + 7 && j < cnt; ++ j)
		{
			tmp = dis(temp[i], temp[j]);
			if (tmp < Mindis)
			{
				Mindis = tmp;
			}
		}
	}
	return Mindis;
}
int main()
{
	int n,i,j;
	double minDis;
	while (scanf("%d", &n)==1 && n)
	{
		for (i = 0; i < n; ++ i)
		{
			scanf("%lf%lf", &point[i].x, &point[i].y);
		}
		qsort(point, n, sizeof(point[0]), cmp);
		minDis = findMin(0, n-1);
		if (minDis > 10000)
		{
			printf("INFINITY\n");
		}
		else
			printf("%.4lf\n", minDis);
	}
	return 0;
}

三維問題:
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>

using namespace std;

class Point
{
public:
	double x;
	double y;
	double z;
};

// [low, high]
int randomInteger(int low, int high)
{
	return low+rand()%(high-low+1);
}

void swap(double & a, double & b)
{
	double tmp=a;
	a=b;
	b=tmp;
}

int quickSortPartition(Point * p, int low, int high)
{
	int random=randomInteger(low, high);
	swap(p[random], p[low]);
	int i, j;
	i=low, j=high;
	double pivot=p[low].y;
	for(j=low; j<=high; j++)
	{
		if(pivot>p[j].y)
		{
			i=i+1;
			swap(p[i], p[j]);
		}
	}
	swap(p[i], p[low]);
	return i;
}


int partition(double * p, int len, int low, int high)
{
	int i, j;
	int random=randomInteger(low, high);
	swap(p[low], p[random]);
	i=low, j=high;
	double pivot=p[low];
	for(j=low; j<=high; j++)
	{
		if(pivot>p[j])
		{
			i=i+1;
		}
	}
	swap(p[random], p[low]);
	return i;
}

void quickSort(Point * p, int low, int high)
{
	if(low<high)
	{
		int par=quickSortPartition(p, low, high);
		quickSort(p, low, par-1);
		quickSort(p, par+1, high);
	}
}

double findMiddle(double * p, int len, int ith)
{
	if(len==1)
		return p[0];
	int random=partition(p, len, 0, len-1);
	if(random == ith)
	{
		return p[random];
	}
	else if(random < ith)
	{
		return findMiddle(p+random+1, len-random-1, ith-random-1);
	}
	else
	{
		return findMiddle(p, random, ith);
	}
}



double Distance(const Point & p1, const Point & p2)
{
	return sqrt( (p1.x-p2.x)*(p1.x-p2.x) + (p1.y-p2.y)*(p1.y-p2.y) + (p1.z-p2.z)*(p1.z-p2.z) );
}

double findMinDistance(Point * p, double * arrayX, double * arrayY, double * arrayZ, int n, Point & des1, Point & des2)
{
	double minDistance=INT_MAX;
	if(n<=3)
	{
		for(int i=0; i<n; i++)
		{
			for(int j=0; j<n; j++)
			{
				if(j != i)
				{
					if(Distance(p[i], p[j])<minDistance)
					{
						minDistance=Distance(p[i], p[j]);
						des1=p[i], des2=p[j];
					}
				}
			}
			return minDistance;
		}
	}
	else
	{
		double middleY=findMiddle(arrayY, n, (int)floor((double)(n/2)));
		// partition P set into PL and PR according to the middle value of y
		Point * pL=new Point[n];
		Point * pR=new Point[n];
		int lIndex=0, rIndex=0;
		double * leftArrayX=new double[n];
		double * leftArrayY=new double[n];
		double * leftArrayZ=new double[n];
		double * rightArrayX=new double[n];
		double * rightArrayY=new double[n];
		double * rightArrayZ=new double[n];
		for(int i=0; i<n; i++)
		{
			if(p[i].y<middleY)
			{
				pL[lIndex]=p[i];
				leftArrayX[lIndex]=p[i].x;
				leftArrayY[lIndex]=p[i].y;
				leftArrayZ[lIndex]=p[i].z;
				lIndex++;
			}
			else
			{
				pR[rIndex]=p[i];
				rightArrayX[rIndex]=p[i].x;
				rightArrayY[rIndex]=p[i].y;
				rightArrayZ[rIndex]=p[i].z;
				rIndex++;
			}
		}
		// divide and conquer
		double lMinDistance=findMinDistance(pL, leftArrayX, leftArrayY, leftArrayZ, lIndex, des1, des2);
		double rMinDistance=findMinDistance(pR, leftArrayX, leftArrayY, leftArrayZ, rIndex, des1, des2);
		double currentMinDistance=lMinDistance<rMinDistance ? lMinDistance : rMinDistance;
		Point * middleP=new Point[n];
		double * middleArrayY=new double[n];
		int middleIndex=0;
		for(int i=0; i<n; i++)
		{
			if(fabs(p[i].y-middleY)<=currentMinDistance)
			{
				middleP[middleIndex]=p[i];
				middleArrayY[middleIndex]=p[i].y;
			}
		}
		for(int i=0; i<middleIndex; i++)
		{
			for(int j=1; j<=15; j++)
			{
				if(Distance(p[i], p[i+j])<currentMinDistance)
				{
					currentMinDistance=Distance(p[i], p[i+j]);
					des1=p[i];
					des2=p[i+j];
				}
			}
		}
		minDistance=currentMinDistance;
		delete [] pL;
		delete [] pR;
		delete [] leftArrayX;
		delete [] leftArrayY;
		delete [] leftArrayZ;
		delete [] rightArrayX;
		delete [] rightArrayY;
		delete [] rightArrayZ;
		delete [] middleP;
		delete [] middleArrayY;
		return minDistance;
	}
}

int main()
{
	Point * p=new Point[4];
	for(int i=0; i<4; i++)
	{
		p[i].x=4-i;
		p[i].y=4-i;
		p[i].z=4-i;
	}
	quickSort(p, 0, 3);
	double * arrayX, * arrayY, * arrayZ;
	arrayX=new double[4];
	arrayY=new double[4];
	arrayZ=new double[4];
	for(int i=0; i<4; i++)
	{
		arrayX[i]=p[i].x;
		arrayY[i]=p[i].y;
		arrayZ[i]=p[i].z;
	}
	for(int i=0; i<4; i++)
	{
		cout<<p[i].x<<" "<<p[i].y<<" "<<p[i].z<<endl;
	}
	double minDistance;
	Point des1, des2;
	minDistance=findMinDistance(p, arrayX, arrayY, arrayZ, 4, des1, des2);
	cout << "The minimal distance is:" << minDistance <<endl;
	cout << "The two points that have the minimal distance is:" << endl;
	cout << "("<<des1.x<<","<<des1.y<<","<<des1.z<<")"<<endl;
	cout << "("<<des2.x<<","<<des2.y<<","<<des2.z<<")"<<endl;
	getchar();
	return 0;
}