1. 程式人生 > >java實現分治法,求平面內最近點對

java實現分治法,求平面內最近點對

演算法分析:

方法一:窮舉

1)演算法描述:已知集合S中有n個點,一共可以組成n(n-1)/2對點對,蠻力法就是對這n(n-1)/2對點對逐對進行距離計算,通過迴圈求得點集中的最近點對
2)演算法時間複雜度:演算法一共要執行 n(n-1)/2次迴圈,因此演算法複雜度為O(n2)

程式碼實現:

利用兩個for迴圈可實現所有點的配對,每次配對算出距離然後更新最短距離.

方法二:分治

   在二維空間裡,可用分治法求解最近點對問題。預處理:分別根據點的x軸和y軸座標進行排序,得到X和Y,很顯然此時X和Y中的點就是S中的點。

情況(1):點數小於等於三時:

                                

情況(2):點數大於三時:      首先劃分集合S為SL和SR,使得SL中的每一個點位於SR中每一個點的左邊,並且SL和SR中點數相同。分別在SL和SR中解決最近點對問題,得到DL和DR,分別表示SL和SR中的最近點對的距離。令d=min(DL,DR)。如果S中的最近點對(P1,P2)。P1、P2兩點一個在SL和一個在SR中,那麼P1和P2一定在以L為中心的間隙內,以L-d和L+d為界,如下圖所示:

                       

     如果在SL中的點P和在SR中的點Q成為最近點對,那麼P和Q的距離必定小於d。因此對間隙中的每一個點,在合併步驟中,只需要檢驗yp+d和yp-d內的點即可。 步驟1:根據點的y值和x值對S中的點排序。 步驟2:找出中線L將S劃分為SL和SR 步驟3:將步驟2遞迴的應用解決SL和SR的最近點對問題,並令d=min(dL,dR)。 步驟4:將L-d~L+d內的點以y值排序,對於每一個點(x1,y1)找出y值在y1-d~y1+d內的所有點,計算距離為d'。                 如果d'小於d,令d=d',最後的d值就是答案。

程式碼如下:

import java.util.Arrays;
// 提供一個靜態方法返回平面內最近點對:public static Point[] nearest(Point[] array)
// 其他的方法皆為私有的助手方法
public class NearestPoint {
	// 返回給定點集中的最近點對,如果只有一個點,則返回只包含一個點的陣列
	public static Point[] nearest(Point[] array) {
		Point[] cpArray = Arrays.copyOfRange(array, 0, array.length);
		SortComparable.sort(cpArray);
		return mergeNearest(cpArray, 0, cpArray.length - 1);
	}

	// 分治求最近點對
	private static Point[] mergeNearest(Point[] points, int begin, int end) {
		if (begin == end)
			return new Point[] { points[begin] };
		else {
			int mid = (begin + end) / 2;
			Point[] left = mergeNearest(points, begin, mid);
			Point[] right = mergeNearest(points, mid + 1, end);
			return merge(left, right, points, begin, mid, end);
		}
	}
	// 合併左右兩邊,求最近點對
	private static Point[] merge(Point[] left, Point[] right, Point[] points,
			int begin, int mid, int end) {
		if (left.length == 1 && right.length == 1)
			return new Point[] { left[0], right[0] };
		Point[] partNearest = partNearest(left, right);
		return mergeNearest(points, partNearest, begin, mid, end);
	}
	// 比較左右兩邊,返回區域性最近點對
	private static Point[] partNearest(Point[] left, Point[] right) {
		// 兩個陣列長度不可能同時為0
		if (left.length == 1) {
			return right;
		} else if (right.length == 1) {
			return left;
		} else {
			if (left[0].distance(left[1]) < right[0].distance(right[1]))
				return left;
			else
				return right;
		}
	}

	private static Point[] mergeNearest(Point[] points, Point[] partNearest,
			int begin, int mid, int end) {
		// 初始化兩邊在d範圍內的點, 並按y座標排序
		Point[] left = null;
		Point[] right = null;
		Point[] nearest = partNearest;
		double middle = (points[mid].getX() + points[mid + 1].getX()) / 2;
		double d = partNearest[0].distance(partNearest[1]);
		for (int i = mid; i >= begin - 1; i--) {
			if (i != begin - 1) {
				if (middle - points[i].getX() <= d)
					continue;
			} else {
				left = Arrays.copyOfRange(points, i + 1, mid + 1);
				SortComparable.sort(left, new PointYComparaotr());
				break;
			}
		}
		for (int i = mid + 1; i <= end + 1; i++) {
			if (i != end + 1) {
				if (points[i].getX() - middle <= d)
					continue;
			} else {
				right = Arrays.copyOfRange(points, mid + 1, i);
				SortComparable.sort(right, new PointYComparaotr());
				break;
			}
		}
		// 遍歷left陣列,在right中尋找符合條件的點
		for (Point inLeft : left) {
			for (int i = 0; i < right.length; i++) {
				if (inLeft.getY() - right[i].getY() > d)
					continue;
				if (inLeft.getY() - right[i].getY() < -d)
					break;
				if (inLeft.distance(right[i]) < d) {
					nearest = new Point[] { inLeft, right[i] };
					d = inLeft.distance(right[i]);
				}
			}
		}
		return nearest;
	}
}
測試程式碼:方法一和方法二輸出一樣的距離則輸出true
public class TestNearestPoints {
	static final int SIZE = 100;
	public static void main(String[] args) {
		PointGenerator pg = new PointGenerator(10000);
		Point[] array = new Point[SIZE];
		for(int i = 0; i < array.length; i++)
			array[i] = pg.next();
		Point[] nearP = NearestPoint.nearest(array);
		double d = nearP[0].distance(nearP[1]);
		System.out.println(nearest(array) == d);
	}

	private static double nearest(Point[] array) {
		double d = 214748364;          // the guard
		for(int i = 0; i < array.length - 1; i++)
			for(int j = i + 1; j < array.length; j++)
				if(d > array[i].distance(array[j])) {
					d = array[i].distance(array[j]);
				}	
		return d;
	}
}
測試程式碼中相關的PointGenerator類等,請參見:http://blog.csdn.net/qq_35328850/article/details/56279113