java實現分治法,求平面內最近點對
阿新 • • 發佈:2019-01-28
演算法分析:
方法一:窮舉
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):點數小於等於三時:
如果在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