zoukankan      html  css  js  c++  java
  • 最接近点对问题_分治法

    一、  问题描述

        给定平面上的n个点,找其中的一对点,使得在n个点组成的所有点对中该点对间的距离最小。

    二、  解题思路及所选算法策略的可行性分析

    思路:利用分治法来解决问题。递归子结构求最接近点对总体可分为几个步骤:

    1、当问题规模小于20,直接求解最小点对

    2、将n个点组成的集合S分成2个子集S1和S2

    3、递归求出两个子集中的最接近点对并比较出最小点对,记录距离dmin

    4、以X坐标为基准找到所有点中线,在中线附近找出相距可能小于dmin的点对点,记录于S3和S4

    5、求S3和S4每点对距离,和dmin进行比较,求解最接近点对

    策略可行性分析:

        设直线l上有2m个点,以m为中点将l分割成两条线段dl,dr,然后求出dl和dr这两点条线段中的最小点距d1,d2,此时d=min{d1,d2},再通过计算出dl线段的中最大点与dr线段中的最小点之间的距离D,最小距离则为min{d,D}. 

         二维情况与此相似,最大的区别只是对于中线位置的点,二维情况只是针对中线旁好多可能点,再在Y轴方向上进行点的筛选,以减少平方计算次数。

    三、  伪代码描述及复杂度分析

    Public static double closestPoint(S)

    {

       1、首先,递归结束条件,当数组长度在一定范围内时直接求出最近点,蛮力求解

       2、求所有点在X坐标中的中位数midX

       3、以midX为界将所有点分成两组分别存放在两个表中

       4、将两张表转化为数组类型,并分别按X坐标升序排列

       5、求S1中的最近距离的两个点

       6、求S2中的最近距离的两个点

       7、求两最近距离的最小值

       8、在S1、S2中收集距离中线小于d1的点,分别存放于两个表中

       9、分别将表T3、T4转换为数组类型的S3、S4,并将其分别按Y坐标升序排列

       10、求解S3、S4两者之间可能的更近(相比于d1)距离 , 以及构成该距离的点

    }

     

    复杂度分析:

         设算法耗时T(n)。 算法第1步、第2步、第3步和第8步用了O(n)时间。第7步和第10步用了常数时间。第4步和第9步用了O(nlogn)时间。第5步和第6步分别用了T(n/2)时间。不过第4步和第9步是数组的排序预处理时间,所以不算在算法中。所以经由预处理的算法所需计算时间满足递归方程:

    T(n)={   O(1)          n<4

            2T(n/2)+O(n)   n>=4

    由此,T(n)=O(nlogn)。

    代码实现

    dcPoint.java

    package 分治法;
    
    public class dcPoint implements Cloneable, Comparable<dcPoint>{
    	public dcPoint() {
    		x = 0;
    		y = 0;
    	}
     
    	public dcPoint(int x, int y) {
    		this.x = x;
    		this.y = y;
    	}
     
    	public void setX(int x) {
    		this.x = x;
    	}
     
    	public void setY(int y) {
    		this.y = y;
    	}
     
    	public int getX() {
    		return x;
    	}
     
    	public int getY() {
    		return y;
    	}
     
    	private int x;
    	private int y;
     
    	@Override
    	public int compareTo(dcPoint o) {
    		if(x == o.getX() && y == o.getY())
    			return 0;
    		else 
    			return 1;
    	}
    }
    

    NPointPair.java

    package 分治法;
    
    import java.util.ArrayList;
    import java.util.Random;
    import java.util.Set;
    import java.util.TreeSet;
     
    public class NPointPair {
    	/**
    	 * 最近点问题
    	 * @param S
    	 */
    	public static dcPoint[] closestPoint(dcPoint [] S){
    		
    		dcPoint[] result = new dcPoint[2];	
    		/**
    		 * 0.首先,解决该问题的边界,当数组长度在一定范围内时直接求出最近点,蛮力求解 
    		 */
    		double dmin = Double.POSITIVE_INFINITY;
    		double tmpmin = 0;
    		if(S.length <= 20){
    			for(int i = 0; i < S.length; i ++){
    				for(int j = i + 1; j < S.length; j ++){
    					tmpmin = Math.sqrt(Math.pow(S[i].getX() - S[j].getX(), 2)) + Math.pow(S[i].getY() - S[j].getY(), 2);
    					if(tmpmin < dmin){
    						dmin = tmpmin;
    						result[0] = S[i];
    						result[1] = S[j];
    					}
    				}
    			}
    			return result;
    		}
    		
    		/**
    		 *1.求所有点在X坐标的中位数 
    		*/
    		int minX = (int) Double.POSITIVE_INFINITY;		//保证假设的初始最小值足够大
    		int maxX = (int) Double.NEGATIVE_INFINITY;		//保证假设的初始最大值足够小
    		for(int i = 0; i < S.length; i++){
    			if(S[i].getX() < minX)
    				minX = S[i].getX();
    			if(S[i].getX() > maxX)
    				maxX = S[i].getX();
    		}
    		int midX = (minX + maxX)/2;
    		
    		/**
    		 * 2.以midX为界将所有点分成两组分别存放在两个表中
    		 */
    		ArrayList T1 = new ArrayList();
    		ArrayList T2 = new ArrayList();
    		for(int i = 0; i < S.length; i++){
    			if(S[i].getX() <= midX)		//是否要=号?
    				T1.add(S[i]);
    			if(S[i].getX() > midX)
    				T2.add(S[i]);
    		}
    		
    		/**
    		 * 3.将两张表转化为数组类型,并分别按X坐标升序排列
    		 */
    		dcPoint [] S1 = new dcPoint[T1.size()];
    		dcPoint [] S2 = new dcPoint[T2.size()];
    		
    		T1.toArray(S1);
    		T2.toArray(S2);
    		
    		mergeSort(S1, "x");		//按X坐标升序排列
    		mergeSort(S2, "x");		//按X坐标升序排列
    		
    		/**
    		 * 4.求S1中的最近距离的两个点
    		 */
    		dcPoint[] result1 = new dcPoint[2];
    		result1 = closestPoint(S1);
    		
    		/**
    		 * 5.求S2中的最近距离的两个点
    		 */
    		dcPoint[] result2 = new dcPoint[2];
    		result2 = closestPoint(S2);
    		
    		/**
    		 * 6.求两最近距离的最小值
    		 */
    		double d1 = Math.sqrt(Math.min(Math.pow(result1[0].getX() - result1[1].getX(), 2) + Math.pow(result1[0].getY() - result1[1].getY(), 2), 
    				Math.pow(result2[0].getX() - result2[1].getX(), 2) + Math.pow(result2[0].getY() - result2[1].getY(), 2)));
    		
    		if(Math.pow(result1[0].getX() - result1[1].getX(), 2) + Math.pow(result1[0].getY() - result1[1].getY(), 2) < 
    				Math.pow(result2[0].getX() - result2[1].getX(), 2) + Math.pow(result2[0].getY() - result2[1].getY(), 2))
    			result = result1;
    		else
    			result = result2;
    		
    		/**
    		 * 7.在S1、S2中收集距离中线小于d1的点,分别存放于两个表中
    		 */
    		ArrayList T3 = new ArrayList();
    		ArrayList T4 = new ArrayList();
    		for(int i = 0; i < S1.length; i++){
    			if(midX - S1[i].getX() < d1)
    				T3.add(S1[i]);
    		}
    		for(int i = 0; i < S2.length; i++){
    			if(S2[i].getX() - midX < d1){
    				T4.add(S2[i]);
    			}
    		}
    		
    		/**
    		 * 8.分别将表T3、T4转换为数组类型的S3、S4,并将其分别按Y坐标升序排列
    		 */
    		dcPoint [] S3 = new dcPoint [T3.size()];
    		dcPoint [] S4 = new dcPoint [T4.size()];
    		T3.toArray(S3);
    		T4.toArray(S4);
    		
    		mergeSort(S3, "y");
    		mergeSort(S4, "y");
    		
    		/**
    		 * 求解S3、S4两者之间可能的更近(相比于d1)距离 , 以及构成该距离的点
    		 */
    		double d =  Double.POSITIVE_INFINITY;
    		for(int i = 0; i < S3.length; i ++){
    			for(int j = 0; j < S4.length; j ++){
    				if(Math.abs(S3[i].getY() - S4[j].getY()) < d1){
    					double tmp = Math.sqrt(Math.pow(S3[i].getX() - S4[j].getX(), 2) + Math.pow(S3[i].getY() - S4[j].getY(), 2));
    					if(tmp < d){
    						d = tmp;
    						result[0] = S3[i];
    						result[1] = S4[j];
    					}
    				} 
    			}
    		}
    		
    		return result;
    	}
    	//归并排序
    	private static void mergeSort(dcPoint[] a, String property){
    		dcPoint[] tempArray = new dcPoint[a.length];
    		mergeSort(a, tempArray, 0, a.length - 1, property);
    	}
    	
    	private static void mergeSort(dcPoint[] a, dcPoint [] tempArray, int left, int right, String property){
    		if(left < right){
    			int center = (left + right) >> 1;
    			//分治
    			mergeSort(a, tempArray, left, center, property);
    			mergeSort(a, tempArray, center + 1, right, property);
    			//合并
    			merge(a, tempArray, left, center + 1, right, property);
    		}
    	}
    	
    	private static void merge(dcPoint [] a, dcPoint [] tempArray, int leftPos, int rightPos, int rightEnd, String property){
    		int leftEnd = rightPos - 1;
    		int numOfElements = rightEnd - leftPos + 1;
    		
    		int tmpPos = leftPos;		//游标变量, 另两个游标变量分别是leftPos 和 rightPos
    		
    		while(leftPos <= leftEnd && rightPos <= rightEnd){
    			if(property.equals("x")){
    				if(a[leftPos].getX() <= a[rightPos].getX())
    					tempArray[tmpPos++] = a[leftPos++];
    				else
    					tempArray[tmpPos++] = a[rightPos++];
    			}else if(property.equals("y")){
    				if(a[leftPos].getY() <= a[rightPos].getY())
    					tempArray[tmpPos++] = a[leftPos++];
    				else
    					tempArray[tmpPos++] = a[rightPos++];
    			}else
    				throw new RuntimeException();
    		}
    		
    		while(leftPos <= leftEnd)
    			tempArray[tmpPos++] = a[leftPos++];
    		while(rightPos <= rightEnd)
    			tempArray[tmpPos++] = a[rightPos++];
    		
    		//将排好序的段落拷贝到原数组中
    		System.arraycopy(tempArray, rightEnd-numOfElements+1, a, rightEnd-numOfElements+1, numOfElements);
    	}
    	
    	public static void main(String[] args) {
    		
    		Set<dcPoint> testData = new TreeSet<dcPoint>();
    		
    		Random random = new Random();
    		
    		int x = 0;
    		int y = 0;
    		
    		for(int i = 0;i < 50;i++){
    			x = random.nextInt(500);
    			y = random.nextInt(500);
    			System.out.println("x:" + x + "  y:" + y);
    			testData.add(new dcPoint(x, y));
    		}
    		
    		dcPoint [] S = new dcPoint[testData.size()];
    		S = (dcPoint[]) testData.toArray(S);
    		
    		for(int i = 0; i < S.length; i ++){
    			System.out.println("(" + S[i].getX() + ", " + S[i].getY() + ")");
    		}
    		
    		System.out.println(testData.size());
    		
    		dcPoint [] result = new dcPoint [2];
    		
    		result = closestPoint(S);
    		
    		System.out.println("最近的两点分别是(" + result[0].getX() + ", " + result[0].getY() 
    				+ ") 和 (" + result[1].getX() + ", " + result[1].getY() + "), 最近距离为:" 
    				+ Math.sqrt(Math.pow(result[0].getX() - result[1].getX(), 2) + Math.pow(result[0].getY() - result[1].getY(), 2)));
    		
    	}
    }
    

      

  • 相关阅读:
    在 Mac 上使用 PICT 进行 Pairwise 测试
    组合测试术语:Pairwise/All-Pairs、OATS(Orthogonal Array Testing Strategy)
    小白懂算法之二分查找
    小白也能看懂的JVM内存区域
    小白也能看懂的JDK1.8前_HashMap的扩容机制原理
    小白也能看懂的ArrayList的扩容机制
    activenq整合spring之队列消费者
    activemq整合spring之队列生产者
    ActiveMQ之Broker
    ActiveMQ_JMS签收
  • 原文地址:https://www.cnblogs.com/LieYanAnYing/p/11777638.html
Copyright © 2011-2022 走看看