-3

我試圖創建一個從隨機生成的點返回最接近的對的算法。我已經完成了算法,但算法的分治法並不比brute-force方法快得多。我可以做些什麼來優化代碼,使它在(n log n)時間返回?劃分並征服最近對算法

import java.util.*; 
import java.lang.*; 
import static java.lang.Math.min; 
import static java.lang.StrictMath.abs; 

public class closestPair { 

    private static Random randomGenerator; // for random numbers 

    public static class Point implements Comparable<Point> { 

     public long x, y; 

     // Constructor 
     public Point(long x, long y) { 
      this.x = x; 
      this.y = y; 
     } 

     public int compareTo(Point p) { 
      // compare this and p and there are three results: >0, ==0, or <0 
      if (this.x == p.x) { 
        if (this.y == p.y) 
         return 0; 
        else 
         return (this.y > p.y)? 1 : -1; 
       } 
      else 
        return (this.x > p.x)? 1 : -1; 
     } 

     public String toString() { 
      return " ("+Long.toString(this.x)+","+Long.toString(this.y)+")"; 
     } 

     public double distance(Point p) { 
      long dx = (this.x - p.x); 
      long dy = (this.y - p.y); 
      return Math.sqrt(dx*dx + dy*dy); 
     } 
    } 

    public static Point[] plane;   

     public static Point[] T; 

     public static Point[] Y; 

    public static int N; // number of points in the plane 

    public static void main(String[] args) { 

     // Read in the Size of a maze 
     Scanner scan = new Scanner(System.in);   
     try {   
      System.out.println("How many points in your plane? "); 
      N = scan.nextInt(); 
     } 
     catch(Exception ex){ 
      ex.printStackTrace(); 
     } 
     scan.close(); 

     // Create plane of N points. 
     plane = new Point[N]; 
       Y = new Point[N]; 
       T = new Point[N]; 
     randomGenerator = new Random(); 

     for (int i = 0; i < N; ++i) { 
      long x = randomGenerator.nextInt(N<<6); 
      long y = randomGenerator.nextInt(N<<6); 
      plane[i] = new Point(x, y); 
     } 
     Arrays.sort(plane); // sort points according to compareTo. 
     for (int i = 1; i < N; ++i) // make all x's distinct. 
      if (plane[i-1].x >= plane[i].x) plane[i].x = plane[i-1].x + 1; 

        //for (int i = 1; i < N; i++) 
         //  if (plane[i-1].y >= plane[i].y) plane[i].y = plane[i-1].y + 1; 
          // 
          //  
     System.out.println(N + " points are randomly created.");   
     System.out.println("The first two points are"+plane[0]+" and"+plane[1]); 
     System.out.println("The distance of the first two points is "+plane[0].distance(plane[1])); 


       long start = System.currentTimeMillis(); 
     // Compute the minimal distance of any pair of points by exhaustive search. 
     double min1 = minDisSimple(); 
       long end = System.currentTimeMillis(); 
     System.out.println("The distance of the two closest points by minDisSimple is "+min1); 
       System.out.println("The running time for minDisSimple is "+(end-start)+" mms"); 
     // Compute the minimal distance of any pair of points by divide-and-conquer 
     long start1 = System.currentTimeMillis(); 
       double min2 = minDisDivideConquer(0, N-1); 
       long end1 = System.currentTimeMillis(); 

     System.out.println("The distance of the two closest points by misDisDivideConquer is "+min2); 
       System.out.println("The running time for misDisDivideConquer is "+(end1-start1)+" mms"); 

     }  

    static double minDisSimple() { 
     // A straightforward method for computing the distance 
     // of the two closest points in plane[0..N-1]. 

     // to be completed 
      double midDis = Double.POSITIVE_INFINITY; 
     for (int i = 0; i < N - 1; i++) { 
       for (int j = i + 1; j < N; j++) { 
        if (plane[i].distance(plane[j]) < midDis){ 
         midDis = plane[i].distance(plane[j]); 
        }    
       } 
      } 
     return midDis; 

     } 


     static void exchange(int i, int j) { 
     Point x = plane[i]; 
     plane[i] = plane[j]; 
     plane[j] = x; 
     } 

    static double minDisDivideConquer(int low, int high) { 
      // Initialize necessary values 
      double minIntermediate; 
      double minmin; 
      double minDis; 

     if (high == low+1) { // two points 
       if (plane[low].y > plane[high].y) exchange(low, high); 
       return plane[low].distance(plane[high]); 
     } 
      else if (high == low+2) { // three points 
      // sort these points by y-coordinate 
      if (plane[low].y > plane[high].y) exchange(low, high); 
      if (plane[low].y > plane[low+1].y) exchange(low, low+1); 
      else if (plane[low+1].y > plane[high].y) exchange(low+1, high); 
      // compute pairwise distances 
      double d1 = plane[low].distance(plane[high]); 
      double d2 = plane[low].distance(plane[low+1]); 
      double d3 = plane[low+1].distance(plane[high]); 
      return ((d1 < d2)? ((d1 < d3)? d1 : d3) : (d2 < d3)? d2 : d3); // return min(d1, d2, d3) 
     } else { // 4 or more points: Divide and conquer 
       int mid = (high + low)/2; 
       double lowerPartMin = minDisDivideConquer(low,mid); 
       double upperPartMin = minDisDivideConquer(mid+1,high); 
       minIntermediate = min(lowerPartMin, upperPartMin); 
       int k = 0; 
       double x0 = plane[mid].x; 
       for(int i = 1; i < N; i++){ 
        if(abs(plane[i].x-x0) <= minIntermediate){ 
         k++; 
         T[k] = plane[i]; 
        } 
       } 
       minmin = 2 * minIntermediate; 
       for (int i = 1; i < k-1; i++){ 
        for(int j = i + 1; j < min(i+7,k);j++){ 
         double distance0 = abs(T[i].distance(T[j])); 
         if(distance0 < minmin){ 
          minmin = distance0; 
         } 
        } 
       } 
       minDis = min(minmin, minIntermediate); 
      } 
      return minDis; 
     } 
    } 
+1

這不是一個代碼評論網站 – redFIVE

+1

我投票結束這個問題作爲題外話,因爲StackOverflow不是一個評論網站。您應該將代碼發佈在[codereview](http://codereview.stackexchange.com/)上。 –

+0

@SaschaKolberg它* *可以被重寫以適合[codereview.se],但是CR問題應該對代碼的任何和所有方面開放反饋*;請參閱CR.Meta上的[Stack Overflow用戶代碼檢查指南](http://meta.codereview.stackexchange.com/q/5777/23788)。 –

回答

1

對minDisSimple的更改使用以下方法。你可以獲得更多的表現。

static double minDisSimple() { 
    // A straightforward method for computing the distance 
    // of the two closest points in plane[0..N-1]. 

    // to be completed 
    double midDis = Double.POSITIVE_INFINITY; 
    double temp; 
    for (int i = 0; i < N - 1; i++) { 
     for (int j = i + 1; j < N; j++) { 
      temp = plane[i].distance(plane[j]); 
      if (temp < midDis) { 
       midDis = temp; 
      } 
     } 
    } 
    return midDis; 

} 

性能明智的少量點簡單的方法是好的,但點數量較大分而治之是好的。嘗試使用10,100,1000,10000,100000,1000000點數。

0

minDisDivideConquer()中的一個關鍵方面是,構建輔助數組T的循環遍歷所有的點。由於總共有O(N)遞歸調用,每次通過所有N點導致複雜度爲O(N^2),與簡單算法相當。

循環實際上應該只考慮指數在lowhigh之間的點。此外,它可以分成兩個獨立的循環,從mid(向前和向後)開始,當檢查距離已經太大時斷開。


minDisDivideConquer()方法的另一個可能的改進,在「4分或以上」的情況,以防止尋找到那些已經在遞歸調用考慮對。

如果我的理解是正確的,則數組T包含那些在x軸上與中點足夠接近的點,以便存在一個機會,即T中的一對點產生的距離小於個體的距離半套。

但是,沒有必要查看在mid之前或兩者都在mid之後的兩個點(因爲這些對在遞歸調用中已被考慮)。

因此,一個可能的優化是構建兩個列表T_leftT_right(代替T)和雙點之間的距離檢查使得一個是上的mid左邊,另一個在右邊。 這樣,我們只會查看|T_left| * |T_right|對,而不是計算|T| * (|T| - 1)/2的距離,而用|T_left| + |T_right| = |T|。該值至多爲(|T|/2) * (|T|/2) = |T|^2/4,,即,其距離比之前少了約2倍(這是最壞的情況,但實際對數也可以小得多,包括零)。