-3

I'm trying to create an algorithm that returns the closest pair from randomly generated points. I have finished the algorithm, however the divide and conquer method of the algorithm is not much faster than the brute-force method. What can I do to optimize the code so that it returns at (n log n) time?

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;
        }
    }
Mathieu Guindon
  • 69,817
  • 8
  • 107
  • 235
SharkMangler
  • 25
  • 1
  • 6
  • 1
    This isn't a code review site – user1231232141214124 Apr 15 '16 at 20:32
  • 1
    I'm voting to close this question as off-topic because StackOverflow is not a review site. You should post your code on [codereview](http://codereview.stackexchange.com/) instead. – Sascha Kolberg Apr 15 '16 at 20:36
  • @SaschaKolberg it *could* be reworded to fit [codereview.se], but CR questions should be open to feedback on *any & all aspects of the code*; please see [Guide to Code Review for Stack Overflow users](http://meta.codereview.stackexchange.com/q/5777/23788) on CR.Meta. – Mathieu Guindon Apr 15 '16 at 20:38

2 Answers2

1

Use the following method with the change for minDisSimple. You can get more performance.

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;

}

Performance wise for small amount of points simple method is good but larger amount of points Divide and Conquer is good. Try number of points with 10, 100, 1000, 10000, 100000, 1000000.

Geeth Lochana
  • 164
  • 13
0

One critical aspect in the minDisDivideConquer() is that the loop that constructs the auxiliary array T iterates through all the N points. Since there are O(N) recursive calls in total, making this pass through all the N points every time leads to a complexity of O(N^2), equivalent to that of the simple algorithm.

The loop should actually only consider the points with indices between low and high. Furthermore, it could be split into two separate loops that start from mid (forward and backward), and break when the checked distance is already too large.


Another possible improvement for the minDisDivideConquer() method, in the "4 or more points" situation is to prevent looking into pairs that were already considered in the recursive calls.

If my understanding is correct, the array T contains those points that are close enough on x axis to the mid point, so that there is a chance that a pair of points in T generates a distance smaller than those from the individual half sets.

However, it is not necessary to look into points that are both before mid, or both after mid (since these pairs were already considered in the recursive calls).

Thus, a possible optimization is to construct two lists T_left and T_right (instead of T) and check distances between pairs of points such that one is on the left of mid, and the other to the right. This way, instead of computing |T| * (|T| - 1) / 2 distances, we would only look into |T_left| * |T_right| pairs, with |T_left| + |T_right| = |T|. This value is at most (|T| / 2) * (|T| / 2) = |T| ^ 2 / 4, i.e. around 2 times fewer distances than before (this is in the worst case, but the actual number of pairs can also be much smaller, inclusively zero).

danbanica
  • 3,018
  • 9
  • 22