分治法-最近距離問題Java實現

來源:互聯網
上載者:User

標籤:des   style   blog   color   java   os   io   strong   

       分治演算法,有很多典型的問題,如最近點問題、線性選擇問題、整數劃分問題、大整數成績問題、棋盤覆蓋問題、迴圈賽議程表、二分搜尋、Strassen矩陣乘法、漢諾塔等。準備花些時間逐個解決這些問題,並用Java實現,從最近點問題開始。網上找到一些代碼,標題如“java 用蠻力法和分治法求解最近對有關問題”,雖然體現了分治,但劃分不夠徹底,因此我重新對其進行了實現。

一、基本思想及策略:

       首先,說說分治的思想。分治, “分而治之”,就是把一個複雜的問題分成兩個或更多的相同或相似的子問題,再把子問題分成更小的子問題……直到最後子問題可以簡單的直接求解,原問題的解即子問題的解的合并。這個技巧是很多高效演算法的基礎,如排序演算法(快速排序,歸併排序),傅立葉變換等。問題的規模越小,越容易直接求解,解題所需的計算時間也越少。

       分治通過減小問題規模,對問題各個擊破,其策略是:對於一個規模為n的問題,若該問題可以容易地解決(比如說規模n較小)則直接解決,否則將其分解為k個規模較小的子問題,這些子問題互相獨立且與原問題形式相同,遞迴地解這些子問題,然後將各子問題的解合并得到原問題的解。

二、分治法適用的情況

    分治法所能解決的問題一般具有以下幾個特徵:

    1 該問題的規模縮小到一定的程度就可以容易地解決

    2 該問題可以分解為若干個規模較小的相同問題,即該問題具有最優子結構性質。

    3 利用該問題分解出的子問題的解可以合并為該問題的解;

    4 該問題所分解出的各個子問題是相互獨立的,即子問題之間不包含公用的子子問題。

第一條特徵是絕大多數問題都可以滿足的,因為問題的計算複雜性一般是隨著問題規模的增加而增加;

第二條特徵是應用分治法的前提它也是大多數問題可以滿足的,此特徵反映了遞迴思想的應用;、

第三條特徵是關鍵,能否利用分治法完全取決於問題是否具有第三條特徵,如果具備了第一條和第二條特徵,而不具備第三條特徵,則可以考慮用貪進法或動態規劃法

第四條特徵涉及到分治法的效率,如果各子問題是不獨立的則分治法要做許多不必要的工作,重複地解公用的子問題,此時雖然可用分治法,但一般用動態規劃法較好

三、分治法的基本步驟

分治法在每一層遞迴上都有三個步驟:

    step1 分解:將原問題分解為若干個規模較小,相互獨立,與原問題形式相同的子問題;

    step2 解決:若子問題規模較小而容易被解決則直接解,否則遞迴地解各個子問題

    step3 合并:將各個子問題的解合并為原問題的解。

四、以最近點問題為例

       最近點對問題:給定平面上的N個點,找出距離最近的兩個點。不仔細分析演算法的效率及最佳化過程,直接說說該問題的解決思路:

             0 如果數組長度(即點的個數)在一定範圍內時直接求出最近點,蠻力求解

             1 求出這些點的X座標的中位元mid

             2 以mid為界將所有點分為兩組,分表放在表T1、T2中

             3 將T1、T2錶轉換為數組S1、S2,並將S1、S2分別按X座標升序排列

             4 求S1中的點的最近距離

             5 求S2中點的最近距離

             6 求4、5中的兩距離的最小值,記為d1

             7 在S1、S2中搜集距離中線(x=mid)小於d1的點,分別存放於表T3、T4中

             8 將表T3、T4轉換為數群組類型S3、S4,並將S3、S4分別按Y座標升序排列

             9 求S3、S4兩者之間可能的最近距離(與d1作比較)

五、代碼參考

 

package ly.ccnu.AlgorithmDesign;import java.util.ArrayList;import java.util.Random;import java.util.Set;import java.util.TreeSet;public class DevideAndConquer {/** * 最近點問題 * @param S */public static Point[] closestPoint(Point [] S){Point[] result = new Point[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座標升序排列 */Point [] S1 = new Point[T1.size()];Point [] S2 = new Point[T2.size()];T1.toArray(S1);T2.toArray(S2);mergeSort(S1, "x");//按X座標升序排列mergeSort(S2, "x");//按X座標升序排列/** * 4.求S1中的最近距離的兩個點 */Point[] result1 = new Point[2];result1 = closestPoint(S1);/** * 5.求S2中的最近距離的兩個點 */Point[] result2 = new Point[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;elseresult = 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座標升序排列 */Point [] S3 = new Point [T3.size()];Point [] S4 = new Point [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(Point[] a, String property){Point[] tempArray = new Point[a.length];mergeSort(a, tempArray, 0, a.length - 1, property);}private static void mergeSort(Point[] a, Point [] 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(Point [] a, Point [] tempArray, int leftPos, int rightPos, int rightEnd, String property){int leftEnd = rightPos - 1;int numOfElements = rightEnd - leftPos + 1;int tmpPos = leftPos;//遊標變數, 另兩個遊標變數分別是leftPos 和 rightPoswhile(leftPos <= leftEnd && rightPos <= rightEnd){if(property.equals("x")){if(a[leftPos].getX() <= a[rightPos].getX())tempArray[tmpPos++] = a[leftPos++];elsetempArray[tmpPos++] = a[rightPos++];}else if(property.equals("y")){if(a[leftPos].getY() <= a[rightPos].getY())tempArray[tmpPos++] = a[leftPos++];elsetempArray[tmpPos++] = a[rightPos++];}elsethrow 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<Point> testData = new TreeSet<Point>();Random random = new Random();int x = 0;int y = 0;for(int i = 0;i < 600;i++){x = random.nextInt(50);y = random.nextInt(50);System.out.println("x:" + x + "  y:" + y);testData.add(new Point(x, y));}Point [] S = new Point[testData.size()];S = (Point[]) 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());Point [] result = new Point [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)));}}

package ly.ccnu.AlgorithmDesign;/** * 建立自己的類Point */class Point implements Cloneable, Comparable<Point>{public Point() {x = 0;y = 0;}public Point(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;@Overridepublic int compareTo(Point o) {if(x == o.getX() && y == o.getY())return 0;else return 1;}}

六、遺留問題

1、TreeSet有重複元

2、堆疊溢位java.lang.StackOverflowError(當點的數量很多,但集中在一塊地區的時候)

若哪位大俠有幸看到並發現解決了問題,還望指教!

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.