1. 程式人生 > >【算法設計與分析基礎】15、最近對問題

【算法設計與分析基礎】15、最近對問題

filename com 算法設計 2個 junit 開始 替換 lis 之間

1、由於Java中沒有存放單個鍵值對的類型使用起來不是很方便

package cn.xf.util;

/**
 * 
 * 功能:相當於一個key value
 * @author xiaofeng
 * @date 2017年6月18日
 * @fileName GenericPair.java
 *
 */
public class GenericPair<E extends Object, F extends Object> {
	
	private E first;
	private F second;
	
	public GenericPair() {
	}
	
	public GenericPair(E first, F second) {
		this.first = first;
		this.second = second;
	}
	
	@Override
	public String toString() {
		String result = "["+ this.first.toString() + ", " + this.second.toString() +"]";
		return result;
	}
	
	public E getFirst() {
		return first;
	}

	public void setFirst(E first) {
		this.first = first;
	}

	public F getSecond() {
		return second;
	}

	public void setSecond(F second) {
		this.second = second;
	}
}

  

求最近鍵值對問題

package cn.xf.algorithm.ch05;

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

import cn.xf.util.GenericPair;

/**
 * 
 * 功能:最近對問題
 * @author xiaofeng
 * @date 2017年6月18日
 * @fileName MinPath.java
 *
 */
public class MinPath {
	
	/**
	 * 
	 * @param points 數組points中存儲了平面上的n >= 2個點,並且按照這些點的x軸坐標升序排序  seqXList  seqYList
	 * @param qpoints 數據qpoint存儲了與points相同的點,只是它是按照這點的Y軸坐標升序排序
	 * 輸出:最近點對之間的歐幾裏得距離
	 */
	public double efficientClosestPair(List<GenericPair<Double, Double>> points, List<GenericPair<Double, Double>> qpoints) {
	    if(points.size() <= 3) {
	        //如果少於3個直接蠻力比較,求幾點的最近距離
	    	double countMin = Double.MAX_VALUE;
	    	for(int i = 0; i < points.size(); ++i) {
	    		for(int j = 0; j < points.size(); ++j) {
	    			if(i == j) {
	    				continue;
	    			}
	    			double temp = dist(points.get(i), points.get(j));
	    			if(temp < countMin) {
	    				countMin = temp;
	    			}
	    		}
	    	}
	        return countMin;
	    } else {
	    	int midIndex = points.size() / 2;
	    	//吧points前面一半提取出來pointsl
	    	List<GenericPair<Double, Double>> pointsl = new ArrayList<GenericPair<Double,Double>>();
	    	for(int i = 0; i < midIndex; ++i) {
	    		pointsl.add(points.get(i));
	    	}
	    	//把qpoints前面一半提取出來qpointsl
//	    	List<GenericPair<Double, Double>> qpointsl = new ArrayList<GenericPair<Double,Double>>();
//	    	for(int i = 0; i < midIndex; ++i) {
//	    		qpointsl.add(qpoints.get(i));
//	    	}
	    	//pointsl 的Y排序版本作為新的qpointsl
	    	List<GenericPair<Double, Double>> qpointsl = new ArrayList<GenericPair<Double,Double>>();
	    	for(int i = 0; i < midIndex; ++i) {
	    		qpointsl.add(points.get(i));
	    	}
	    	
	    	seqYList(qpointsl, 0, qpointsl.size() - 1);
	    	
	    	//把points後面一半提取出來pointsr
	    	List<GenericPair<Double, Double>> pointsr = new ArrayList<GenericPair<Double,Double>>();
	    	for(int i = midIndex; i < points.size(); ++i) {
	    		pointsr.add(points.get(i));
	    	}
	    	//吧qpoints後面一半提取出來qpointsr
//	    	List<GenericPair<Double, Double>> qpointsr = new ArrayList<GenericPair<Double,Double>>();
//	    	for(int i = midIndex; i < qpoints.size(); ++i) {
//	    		qpointsr.add(qpoints.get(i));
//	    	}
	    	
	    	List<GenericPair<Double, Double>> qpointsr = new ArrayList<GenericPair<Double,Double>>();
	    	for(int i = midIndex; i < points.size(); ++i) {
	    		qpointsr.add(points.get(i));
	    	}
	    	
	    	seqYList(qpointsr, 0, qpointsr.size() - 1);
	    	
	    	//用新的集合點進入遞歸
	    	double dl = efficientClosestPair(pointsl, qpointsl);
	    	double dr = efficientClosestPair(pointsr, qpointsr);
	    	
	    	//兩個距離取小的
	    	double dmin = minValue(dl, dr);
	    	double dminDist = Math.pow(dmin, 2);
	    	//取X值得中間分段
	    	double midPointX = points.get(midIndex).getFirst();
	    	//吧所有這些點中X-midPointX的距離比dmin小的提取出來
	    	List<GenericPair<Double, Double>> pointsDmin = new ArrayList<GenericPair<Double,Double>>();
	    	for(int i = 0; i < qpoints.size(); ++i) {
	    		//絕對值比最小的距離還小
	    		if(Math.abs(qpoints.get(i).getFirst() - midPointX) < dmin) {
	    			//比兩邊最小距離,橫向更小的值,這個是按照Y升序的數據
	    			pointsDmin.add(qpoints.get(i));
	    		}
	    	}
	    	
	    	//遍歷這個集合中的所有數據,獲取最小距離
	    	for(int i = 0; i < pointsDmin.size(); ++i) {
	    		//如果K超出範圍,就不用算了
	    		int k = i + 1;
	    		if(k >= pointsDmin.size()) {
	    			break;
	    		}
	    		//求平方差
	    		double minY2 = Math.pow(pointsDmin.get(k).getSecond() - pointsDmin.get(i).getSecond(), 2);
	    		while(k < pointsDmin.size() && minY2 < dminDist) {
	    			//在數據範圍內,然後Y的平方差比最小的還小,那麽就可以進行比較了
	    			double tempMin = Math.pow(pointsDmin.get(k).getFirst() - pointsDmin.get(i).getFirst(), 2) + Math.pow(pointsDmin.get(k).getSecond() - pointsDmin.get(i).getSecond(), 2);
	    			dminDist = minValue(tempMin, dminDist);
	    			++k;
	    		}
	    	}
	    	
	    	return Math.sqrt(dminDist);
	    }
	}
	
	public double minValue(double dl, double dr) {
		if(dl < dr) {
			return dl;
		} else {
			return dr;
		}
	}
	
	public static double dist(GenericPair<Double, Double> point1, GenericPair<Double, Double> point2) {
	    //求出兩點之間的距離
	    //求平方差
	    double distx = Math.abs(point1.getFirst() - point2.getFirst());
	    double disty = Math.abs(point1.getSecond() - point2.getSecond());
	    //求平方差的和的平方根
	    return Math.sqrt(distx * distx + disty * disty);
	}
	
	@Test
	public void quikSortTest() {
		List<GenericPair<Double, Double>> points = new ArrayList<GenericPair<Double,Double>>();
		List<GenericPair<Double, Double>> qpoints = new ArrayList<GenericPair<Double,Double>>();
		//5,3,1,9,8,2,4,7,8
		//4,1,8,2,11,9,7,15,11
		GenericPair<Double, Double> el1 = new GenericPair<Double, Double>(5d, 4d);
		GenericPair<Double, Double> el2 = new GenericPair<Double, Double>(3d, 1d);
		GenericPair<Double, Double> el3 = new GenericPair<Double, Double>(1d, 8d);
		GenericPair<Double, Double> el4 = new GenericPair<Double, Double>(9d, 2d);
		GenericPair<Double, Double> el5 = new GenericPair<Double, Double>(8d, 11d);
		GenericPair<Double, Double> el6 = new GenericPair<Double, Double>(2d, 9d);
		GenericPair<Double, Double> el7 = new GenericPair<Double, Double>(4d, 7d);
		GenericPair<Double, Double> el8 = new GenericPair<Double, Double>(7d, 15d);
//		GenericPair<Double, Double> el9 = new GenericPair<Double, Double>(8d, 11d);
		points.add(el1);points.add(el2);points.add(el3);points.add(el4);points.add(el5);
		points.add(el6);points.add(el7);points.add(el8);//points.add(el9);
		
		qpoints.add(el1);qpoints.add(el2);qpoints.add(el3);qpoints.add(el4);qpoints.add(el5);
		qpoints.add(el6);qpoints.add(el7);qpoints.add(el8);//qpoints.add(el9);
		
		MinPath mp = new MinPath();
		mp.seqXList(points, 0, points.size() - 1);
		mp.seqYList(qpoints, 0, qpoints.size() - 1);
		
//		for(GenericPair<Double, Double> temp : points){
//			System.out.print(temp.toString() + "\t");
//		}
		
		double result = mp.efficientClosestPair(points, qpoints);
		System.out.println(result);
	}
	
	/**
	 * 按照X升序
	 * @param points
	 * @return
	 */
	public static void seqXList(List<GenericPair<Double, Double>> points, int start, int end) {
		//獲取中間點位置,然後一分為二,進行遍歷遞歸
		if(start < end) {
			int mid = hoarePartition(points, false, start, end);
			seqXList(points, start, mid - 1);
			seqXList(points, mid + 1, end);
		}
	}
	
	/**
	 * 按照Y升序
	 * @param points
	 * @return
	 */
	public static void seqYList(List<GenericPair<Double, Double>> points, int start, int end) {
		// 獲取中間點位置,然後一分為二,進行遍歷遞歸
		if (start < end) {
			int mid = hoarePartition(points, true, start, end);
			seqYList(points, start, mid - 1);
			seqYList(points, mid + 1, end);
		}
	}
	
	/**
	 * 尋找分裂點,並規整數據,xOry是用來判定兩邊是用x-false排序還是用y-true排序
	 * @param points
	 * @return
	 */
	public static int hoarePartition(List<GenericPair<Double, Double>> points, boolean xOry, int start, int end) {
		if(start >= end) {
			return start;
		}
		//以第一個數為準對進行分裂
		double temp = getValue(points.get(start), xOry);
		
		//兩邊進行遍歷,直到兩個錯開,或者相等
		int leftIndex = start + 1;
		int rightIndex = end;
		while(leftIndex < rightIndex) {
			//如果遍歷到左邊的比第一個大,右邊比第一個數小,交換數據,為了避免多余的一次交換
			while(getValue(points.get(leftIndex), xOry) < temp) {
				++leftIndex;
			}
			
			while(getValue(points.get(rightIndex), xOry) > temp) {
				--rightIndex;
			}
			//如果是到了最後一步,錯位了,然後交換了,避免多余的一次交換
		    swap(points, leftIndex, rightIndex);
		}
		//從新吧最後一對錯位的數據交換回來,但是如果這個循環根本沒有進去過,那麽就不應該有這個交換
		swap(points, leftIndex, rightIndex);
		//最後把j對應的數據地方交換到開始作為中間點的數據位置
		//如果本身比要排列的中間值還小,那麽不用換,換了就打亂了排序
		if(getValue(points.get(start), xOry) > getValue(points.get(rightIndex), xOry))
		    swap(points, start, rightIndex);
		
		return rightIndex;
	}
	
	//交換數據
	public static void swap(List<GenericPair<Double, Double>> points, int i, int j) {
		//取出i位置的數據,吧i數據修改為j,temp為i的數據,吧j替換為temp
		GenericPair tempi = points.get(i);
		GenericPair tempj = points.get(j);
		points.set(i, tempj);
		points.set(j, tempi);
	}
	
	/**
	 * xOry是用來判定兩邊是用x-false排序還是用y-true排序
	 * @param pair
	 * @param xOry
	 * @return
	 */
	public static double getValue(GenericPair<Double, Double> pair, boolean xOry) {
		double temp = 0;
		if(!xOry) {
			//如果是true,那麽就是判斷y
			//否則是第一個x
			temp = pair.getFirst();
		} else {
			temp = pair.getSecond();
		}
		
		return temp;
	}
}

  

技術分享

最近點距離

技術分享

疑問,求解答,網上什麽 “鴿巢原理” 不是很懂,求通俗點的解釋。。。

技術分享

【算法設計與分析基礎】15、最近對問題