-3

我正在尝试创建一种算法,该算法从随机生成的点中返回最接近的对。我已经完成了算法,但是算法的分治法并不比蛮力法快多少。我可以做些什么来优化代码,使其在 (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;
        }
    }
4

2 回答 2

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 的点数。

于 2016-04-15T20:51:55.417 回答
0

其中一个关键方面minDisDivideConquer()是构造辅助数组的T循环遍历所有N点。由于总共有递归调用,因此每次都O(N)通过所有点导致复杂度为,相当于简单算法的复杂度。NO(N^2)

循环实际上应该只考虑索引在low和之间的点high。此外,它可以分成两个单独的循环,从mid(向前和向后)开始,并在检查的距离已经太大时中断。


在“4 点或更多点”情况下,该方法的另一个可能改进minDisDivideConquer()是防止查看递归调用中已经考虑的对。

如果我的理解是正确的,则数组T包含那些在 x 轴上与中点足够接近的点,因此其中的一对点有可能T产生的距离小于单个半组的距离。

但是,没有必要查看同时位于 beforemid或 after的点mid(因为在递归调用中已经考虑了这些对)。

因此,一种可能的优化是构造两个列表T_leftT_right(而不是T)并检查点对之间的距离,使得一个在 的左侧mid,另一个在右侧。这样|T| * (|T| - 1) / 2,我们只需要|T_left| * |T_right|成对查找,而不是计算距离|T_left| + |T_right| = |T|。该值最多为(|T| / 2) * (|T| / 2) = |T| ^ 2 / 4距离比以前少大约 2 倍(这是最坏的情况,但实际的对数也可以小得多,包括零)。

于 2016-04-15T21:03:22.680 回答