分治法求最近點對( java )

分治法求最近點對問題

這個問題熬了我一個晚上改錯,都是空指針錯誤,從網上看了好多資料都沒什麼用,但就是那一瞬間看到了代碼的某一行發現了錯誤,在數據與數組中某個值比較時,數組範圍弄錯了,導致比較過程中會指向數組外的地方(還是空指針問題)。現寫下分析過程,以便後續查驗
問題描述
在這裏插入圖片描述補充要求:隨機生成橫縱座標值均在1-10的30個點(浮點數),遞歸求出距離最近的一對點的座標及其該兩點距離值並以顯示。
時間複雜度
如果是用蠻力法,那麼時間複雜度毫無疑問是O(n^2),並不是最優解。現採用分治法遞歸函數,使之時間複雜度爲O(nlogn)。
基本思想
30個點爲0到29,數集爲S。
基本思想:將S分成左右不相交的兩個子集,求每個子集的最近點對,以及分別位於兩個子集中的
所有點對的最近點對(由左邊一個點,右邊一個點構成),最後通過比較得到S中的最近點對。
做法
(1)當n≤2時直接求解。n爲1時則返回一個double型的最大值,n爲2時則記錄兩點並返回兩點間距離。n>3時按如下步驟。
(2)分解:根據x座標,對S中的點升序排序,以經過第n/2個點做中垂線,分爲左右兩側,將S均分成兩個子集:S[1…m] 和[m+1…n] ,其中 (int) m=n/2 。這樣,一部分在中垂線的左邊或在中垂線上,另一部分在中垂線右邊或在中垂線上。
(3)遞歸:用同樣的分治算法分別求中垂線兩邊的最近點對的距離min1和min2。
(4)組合:比較min1和min2,取較小值賦給mint,求中垂線左邊一個點和右邊一個點的最近距離看其是否小於mint,若小於則改變點對,更新mint。
難點:求中垂線左邊一個點和右邊一個點的最近距離
如果還是左邊和右邊一個一個算的話,又回到了O(n^2)。事實上不用遍歷每一個點,有些點沒有必要去算。先給出具體方法:我們現已比較出左側和右側兩方的最短距離mint,那麼以中垂線爲界兩邊擴展mint的距離,可能符合要求的點均在這個區域內。這些點可以比較其與中垂線的距離來篩選,x的差距絕對值小於mint的進入新的數組K[n]。如圖:
在這裏插入圖片描述
那麼K中數據怎麼比較呢,現介紹一個方式。
取一個mint×2*mint方框,若其中不存在小於mint的點對,那麼這個方框中最多存在六個點。
在這裏插入圖片描述
所以我們往K裏放數據時應是按y的值從小到大排好的,這樣我們遍歷K的時候只需往後多找六個點就行
了。這樣使得此過程時間複雜度爲O(n)。但是需提前將30個點分別按x和y排好順序,存到兩個數組中,以便後續使用。
上代碼(如有人查看,請不要直接搬抄代碼,無意義)

import java.util.Arrays;
import java.util.Comparator;
public class MinDistance {
   static Points point = new Points();
  public static void main(String[] args) {
    node[] a = new node[30];
    createPoint(a);
    node[] b = a.clone();
    Arrays.sort( a , new cmp1() );  //按x排序
    Arrays.sort( b , new cmp2() );  //按y排序
    double dis = minDis(0,29,a,b,point);    
    System.out.println("最近點對:");
    System.out.println("( "+point.x1+" , "+point.y1+" ),"+"( "+point.x2+" , "+point.y2+" )");
    System.out.println("最近距離:"+dis);
}
   public static void createPoint(node[] a) {     //創建隨機點
    for(int i=0;i<30;i++)
    {
     a[i] = new node();
     a[i].x = 10*Math.random();
     a[i].y = 10*Math.random();
     for(int j=0;j<i;j++) {
      if( a[j].x==a[i].x && a[j].y==a[i].y ) {
       i--;
       break;
      }
     }
    }
   }
   public static double distance(int i,int j,node[] a) {  //計算距離
    double n = Math.pow(a[i].y-a[j].y, 2);   
    double m = Math.pow(a[i].x-a[j].x, 2);
    double w = Math.sqrt(m+n);
    return w;
   }
   public static double minDis(int low,int high,node[] a,node[] b,Points p) {
    if(high-low==0)
     return Double.MAX_VALUE;
    else if(high-low==1) {
     p.x1 = a[low].x;
     p.x2 = a[high].x;
     p.y1 = a[low].y;
     p.y2 = a[high].y;
     return distance(low,high,a);
    }
    else {
     int m = (low+high)/2;
     Points c = new Points();
  Points d = new Points();
     double min1 = minDis(low,m,a,b,c);         
     double min2 = minDis(m+1,high,a,b,d); 
     double mint;
     if(min1 < min2){
   p.x1 = c.x1;
   p.x2 = c.x2;
   p.y1 = c.y1;
   p.y2 = c.y2;
   mint = min1;
  }else {
   p.x1 = d.x1;
   p.x2 = d.x2;
   p.y1 = d.y1;
   p.y2 = d.y2;
   mint = min2;
  }
     double x = a[m].x;
     int k = 0;
     node[] K = new node[30];
     for(int i=0;i<30;i++) {
      if(mint>=(Math.abs(b[i].x-x))) {
       K[k] = new node();
       K[k].x = b[i].x;
       K[k].y = b[i].y;
       k++;
      }
     }
     for(int i=0;i<(k-1);i++) {
      int temp = (i+7)<k?(i+7):k;
      for(int j=i+1;j<temp;j++) {      //記住此地方,空指針,爲了它真是用了好長時間,長記性
       if(distance(i,j,K)<mint) {      
        mint = distance(i,j,K);
        p.x1 = K[i].x;
        p.x2 = K[j].x;
        p.y1 = K[i].y;
        p.y2 = K[j].y;
       }
      }
     }
     return mint;
    }
   }
}
class Points{       // 存儲最近點對
 public double x1;
 public double y1;
 public double x2;
 public double y2;
}
class node{        //存儲30個點
 public double x=0;
 public double y=0;
}
class cmp1 implements Comparator<node>{
 public int compare(node a,node b){
  if( a.x < b.x)
   return -1;
  else if(a.x >b.x)
   return 1;
  else return 0;
 }
}
class cmp2 implements Comparator<node>{
 public int compare(node a,node b){
  if( a.y < b.y)
   return -1;
  else if(a.y >b.y)
   return 1;
  else return 0;
 }
 }

個人總結,如有幫助,感謝點贊,如有問題,下方評論

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章