最近对问题_分治法

S中的点为平面上的点,将平面上的点集分割为点的个数大致相同的两个子集S1和S2,选取垂直线x=c来作为分割线,其中,m为S中各点x坐标的中位数。遵循分治法方法,我们可以递归求出左子集S1和右子集S2中的最近对。设d1和d2分别是S1和S2点对中的最小距离,因为距离最近的两个点可能位于分界线的两侧。所以,在合并较小子问题的解时,需要增加一个步骤来检查是否存在这样的点。显然,我们把这种尝试限制在以x=c为对称的、宽度为2d(d1和d2中的最小值)的垂直带中。

设S1和S2分别是该垂直带左右部分的点构成的子集。现在,对于S1中的,每个点P(x,y),需要检查S2中的点和P之间的距离是否小于d。这种点的y轴坐标一定位于区间[y-d,y+d]中,这样的点最多为6个,因为S2中的点相互之间的距离至少为d。S1和S2中的点按照y坐标升序排序,所以每次迭代时对这些点不必重新排序,只要合并两个已经排好序的集合就可以了。我们可以顺序地处理S1中的点,同时使用一个指针在S2中的宽度为2d的区间中来回移动,取出6个候选点,来计算它们和S1当前点p之间的距离,找到与之距离小于d的点,更新最短距离,直到循环结束,即可求出最短距离。

package 实验二;

public class 最近对问题_分治法 {
	public static void main(String args[]) {
		分治 fz=new 分治();
		fz.init();   //随机点
		fz.showxy(); 
		fz.initSortX();  //按X轴排序
		fz.showxy2();
		fz.zhongjianshu(); //找中间数
		fz.min1();         //找min1
		fz.min2();         //找min2
		fz.compare();      //min1和min2最小的
		fz.SS1();          
		fz.SS2();

		//如果S1或S2有空的,那么最近点肯定在S1或S2中

		if(fz.countSS1==0 || fz.countSS2==0) {
			double min000=fz.min1<fz.min2?fz.min1:fz.min2;
			if(min000==fz.min1) {
				System.out.print("最近点为:");
				System.out.print("("+fz.xy[fz.min1_i_1].x+","+fz.xy[fz.min1_i_1].y+")"+" ");
				System.out.print("("+fz.xy[fz.min1_i_2].x+","+fz.xy[fz.min1_i_2].y+")"+" ");
				System.out.println();
			}
			if(min000==fz.min2) {
				System.out.print("最近点为:");
				System.out.print("("+fz.xy[fz.min2_i_1].x+","+fz.xy[fz.min2_i_1].y+")"+" ");
				System.out.print("("+fz.xy[fz.min2_i_2].x+","+fz.xy[fz.min2_i_2].y+")"+" ");
				System.out.println();
			}
		}
//
		//
//
		else {
			fz.initSortSS1Y();  //对SS1的Y轴排序
			fz.initSortSS2Y();  //对SS2的Y轴排序
			fz.min3();          //找第三个最近点
			fz.Min();           //找所有的最近点
		}
	}
}
class X_Y {
	double x;
	double y;
}
class 分治{
	int length=30;
	X_Y[] xy=new X_Y[length];
	X_Y[] xy2=new X_Y[length];
	X_Y[] SS1;
	X_Y[] SS2;
	X_Y[] SS1_2;
	X_Y[] SS2_2;
	X_Y[] xy4;
	double m=0;   //中间数
	int mi=0;  //中间数的下标
	double min1=999;  //S1的最近点
	int min1_i_1;       //S1的最近点的下标1
	int min1_i_2;       //S1的最近点的下标2
	double min2=999;  //S2的最近点
	int min2_i_1;       //S2的最近点的下标1
	int min2_i_2;       //S2的最近点的下标2
	double min=999;   //S1与S2中最近的点
	double min3=999;  //
	int min3_i_1;       //3的最近点的下标1
	int min3_i_2;       //3的最近点的下标2
	int min_1;          //所有最近点1
	int min_2;          //所有最近点2
	int countSS1=0;
	int countSS2=0;
/
	//初始化
/
	void init() {      
		for(int i=0;i<length;i++) {
			xy[i]=new X_Y();
			xy2[i]=new X_Y();
			xy[i].x=(int)(Math.random()*30);
			xy[i].y=(int)(Math.random()*30);
		}
	}
	void showxy() {
		for(int i=0;i<length;i++) {
			System.out.print("("+xy[i].x+","+xy[i].y+")"+" ");
		}
		System.out.println();
	}
	void showxy2() {
		for(int i=0;i<length;i++) {
			xy[i]=xy2[i];
			System.out.print("("+xy[i].x+","+xy[i].y+")"+" ");
		}
		System.out.println();
	}
//
	//分治排序,按X轴排序
//
	void initSortX(){    
		MergeSortX(xy,xy2,0,length-1);
	}
	void MergeSortX(X_Y xy[],X_Y xy2[],int s,int t){ 
		int m;
		if(s==t)
			xy2[s]=xy[s];
		else {
			m=(s+t)/2;
			MergeSortX(xy,xy2,s,m);     //归并前半个子序列
			MergeSortX(xy,xy2,m+1,t);   //归并后半个子序列
			MergeX(xy2,xy,s,m,t);       //合并两个已排序的子序列
		}
	}
	int MergeX(X_Y xy2[],X_Y xy[],int s,int m,int t){
		for(int a=s;a<=t;a++) {
			xy[a]=xy2[a];
		}
		int i=s; 
		int j=m+1;
		int k=s;
		while(i<=m && j<=t) {    //取较小的放入r1[k]
			if(xy[i].x<=xy[j].x)
				xy2[k++]=xy[i++];
			else
				xy2[k++]=xy[j++];
		}
		if(i<=m) {              //若第一个子序列没处理完,则进行收尾处理
			while(i<=m)
				xy2[k++]=xy[i++];
		}
		else {                  //若第二个子序列没处理完,则进行收尾处理
			while(j<=t)
				xy2[k++]=xy[j++];
		}
		return 0;
	}
///
	找到中间数
///
	void zhongjianshu() {   
		m=(xy[4].x+xy[5].x)/2;
		for(int i=0;i<10;i++) {
			if(xy[i].x<=m) {
				mi=i;
			}
		}
	}
//
	//找到S1的最近距离
//
	void min1() {
		double x;
		for(int i=0;i<=mi;i++) {
			for(int j=i+1;j<=mi;j++) {
				x=Math.sqrt((xy[i].x-xy[j].x)*(xy[i].x-xy[j].x)+(xy[i].y-xy[j].y)*(xy[i].y-xy[j].y));
				if(x<min1) {
					min1=x;
					min1_i_1=i;
					min1_i_2=j;
				}
			}
		}
		System.out.println("S1的最近距离为:"+min1);
	}
//
	//找到S2的最近距离
//
	void min2() {
		double x;
		for(int i=mi+1;i<length;i++) {
			for(int j=i+1;j<length;j++) {
				x=Math.sqrt((xy[i].x-xy[j].x)*(xy[i].x-xy[j].x)+(xy[i].y-xy[j].y)*(xy[i].y-xy[j].y));
				if(x<min2) {
					min2=x;
					min2_i_1=i;
					min2_i_2=j;
				}
			}
		}
		System.out.println("S2的最近距离为:"+min2);
	}
//
	//找到S1和S2的最近距离
//
	void compare() {
		min=min1<min2?min1:min2;
		System.out.println("S1和S2中最近距离为:"+min);
	}
//
	//找到距离中线范围的点(SS1)
//
	void SS1() {
		for(int i=0;i<length;i++) {
			if(xy[i].x>=(m-min) && xy[i].x<=m) {
				countSS1++;
			}
		}
		SS1=new X_Y[countSS1];
		int j=0;
		for(int i=0;i<length;i++) {
			if(xy[i].x>=(m-min) && xy[i].x<=m) {
				SS1[j]=xy[i];
				j++;
			}
		}
		System.out.print("SS1的点为:");
		for(int i=0;i<countSS1;i++) {
			System.out.print("("+SS1[i].x+","+SS1[i].y+")"+" ");
		}
		System.out.println();
	}
//
	//找到距离中线范围的点(SS2)
//
	void SS2() {
		for(int i=0;i<length;i++) {
			if(xy[i].x>m && xy[i].x<=m+min) {
				countSS2++;
			}
		}
		SS2=new X_Y[countSS2];
		int j=0;
		for(int i=0;i<length;i++) {
			if(xy[i].x>m && xy[i].x<=(m+min)) {
				SS2[j]=xy[i];
				j++;
			}
		}
		System.out.print("SS2的点为:");
		for(int i=0;i<countSS2;i++) {
			System.out.print("("+SS2[i].x+","+SS2[i].y+")"+" ");
		}
		System.out.println();
	}
/
	//对SS1按Y轴排序
/
	void initSortSS1Y(){    
		SS1_2=new X_Y[countSS1];
		MergeSortY(SS1,SS1_2,0,countSS1-1);
		System.out.print("排序后的SS1为:");
		for(int i=0;i<countSS1;i++) {
			SS1[i]=SS1_2[i];
			System.out.print("("+SS1[i].x+","+SS1[i].y+")"+" ");
		}
		System.out.println();
	}
/
	//对SS2按Y轴排序
/
	void initSortSS2Y(){    
		SS2_2=new X_Y[countSS2];
		MergeSortY(SS2,SS2_2,0,countSS2-1);
		System.out.print("排序后的SS2为:");
		for(int i=0;i<countSS2;i++) {
			SS2[i]=SS2_2[i];
			System.out.print("("+SS2[i].x+","+SS2[i].y+")"+" ");
		}
		System.out.println();
	}
//
	//
///
	void MergeSortY(X_Y SS1[],X_Y SS1_2[],int s,int t){ 
		int m;
		if(s==t)
			SS1_2[s]=SS1[s];
		else {
			m=(s+t)/2;
			MergeSortY(SS1,SS1_2,s,m);     //归并前半个子序列
			MergeSortY(SS1,SS1_2,m+1,t);   //归并后半个子序列
			MergeY(SS1_2,SS1,s,m,t);       //合并两个已排序的子序列
		}
	}
	int MergeY(X_Y SS1_2[],X_Y SS1[],int s,int m,int t){
		for(int a=s;a<=t;a++) {
			SS1[a]=SS1_2[a];
		}
		int i=s; 
		int j=m+1;
		int k=s;
		while(i<=m && j<=t) {    //取较小的放入r1[k]
			if(SS1[i].y<=SS1[j].y)
				SS1_2[k++]=SS1[i++];
			else
				SS1_2[k++]=SS1[j++];
		}
		if(i<=m) {              //若第一个子序列没处理完,则进行收尾处理
			while(i<=m)
				SS1_2[k++]=SS1[i++];
		}
		else {                  //若第二个子序列没处理完,则进行收尾处理
			while(j<=t)
				SS1_2[k++]=SS1[j++];
		}
		return 0;
	}

	//

	void min3() {
		for(int i=0;i<countSS1;i++) {
			for(int j=0;j<countSS2;j++) {
				if(SS2[j].y>=(SS1[i].y-min) && SS2[j].y<=(SS1[i].y+min) ) {
					min3=Math.sqrt((SS1[i].x-SS2[j].x)*(SS1[i].x-SS2[j].x)+(SS1[i].y-SS2[j].y)*(SS1[i].y-SS2[j].y));
					min3_i_1=i;
					min3_i_2=j;
				}
			}
		}
		System.out.println("第三个最近距离为:"+min3);
	}
	void Min() {
		System.out.println("最近距离为"+min);
		double min=999;
		if(min1<min2) {
			min=min1;
			min_1=min1_i_1;
			min_2=min1_i_2;
		}
		else {
			min=min2;
			min_1=min2_i_1;
			min_2=min2_i_2;
		}
		if(min>min3) {
			min=min3;
			min_1=min3_i_1;
			min_2=min3_i_2;
			System.out.print("最近点为");
			System.out.print("("+SS1[min3_i_1].x+","+SS1[min3_i_1].y+")"+" ");
			System.out.print("("+SS2[min3_i_2].x+","+SS2[min3_i_2].y+")"+" ");
			System.out.println();
		}
		else if(min==min1) {
			System.out.print("最近点为");
			System.out.print("("+xy[min1_i_1].x+","+xy[min1_i_1].y+")"+" ");
			System.out.print("("+xy[min1_i_2].x+","+xy[min1_i_2].y+")"+" ");
			System.out.println();
		}
		else {
			System.out.print("最近点为");
			System.out.print("("+xy[min2_i_1].x+","+xy[min2_i_1].y+")"+" ");
			System.out.print("("+xy[min2_i_2].x+","+xy[min2_i_2].y+")"+" ");
			System.out.println();
		}
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
最近点对问题是指在平面上给定N个点,求其中距离最近的两个点之间的距离。分治法是一种常用的解决最近点对问题的算法。 分治法的基本思想是将问题分解成多个子问题,然后递归地解决这些子问题,最后将子问题的解合并起来得到原问题的解。对于最近点对问题,我们可以采用以下的分治算法: 1. 将所有点按照横坐标从小到大排序,然后将它们划分为两部分,分别处理左右两个部分。 2. 对于左右两个部分,分别递归求解最近点对距离。 3. 将左右两个部分中距离最近的点对的距离记为d。 4. 扫描所有横坐标在中间区域内的点,按照纵坐标从小到大排序。对于每个点,只需考虑它与纵坐标差不超过d的点之间的距离,因为这些点是可能成为最近点对的候选者。 5. 对于每个点,只需要考虑它与后面7个点之间的距离即可,因为如果有距离更近的点,它们之间的距离一定小于d。 6. 扫描完中间区域内的所有点后,最近点对的距离就是左右两个部分中的最小值和中间区域内的最小值中的较小值。 下面是该算法的伪代码: ``` function closest_pair(points): // 按照横坐标排序 sort(points, key=lambda p: p.x) // 递归终止条件 if len(points) <= 3: return brute_force(points) // 求解左右两个部分的最近点对距离 mid = len(points) // 2 left_points = points[:mid] right_points = points[mid:] left_min_dist = closest_pair(left_points) right_min_dist = closest_pair(right_points) // 求解中间区域的最近点对距离 min_dist = min(left_min_dist, right_min_dist) strip_points = [p for p in points if abs(p.x - points[mid].x) < min_dist] strip_points.sort(key=lambda p: p.y) for i in range(len(strip_points)): for j in range(i+1, min(i+8, len(strip_points))): dist = distance(strip_points[i], strip_points[j]) min_dist = min(min_dist, dist) return min_dist ``` 其中,brute_force是暴力求解最近点对距离的函数,distance是计算两个点之间距离的函数。时间复杂度约为O(N log N)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值