【问题标题】:Divide and Conquer Closest Pair Algorithm分而治之最近对算法
【发布时间】:2016-04-15 20:25:53
【问题描述】:

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

【问题讨论】:

  • 这不是代码审查网站
  • 我投票结束这个问题,因为 StackOverflow 不是评论网站。您应该将代码发布到 codereview
  • @SaschaKolberg 可以改写以适应 Code Review,但 CR 问题应该对代码的任何和所有方面开放接受反馈;请参阅 CR.Meta 上的Guide to Code Review for Stack Overflow users

标签: java algorithm optimization divide-and-conquer closest-points


【解决方案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 的点数。

【讨论】:

    【解决方案2】:

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

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


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

    如果我的理解是正确的,数组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 倍(这是最坏的情况,但实际的对数也可以小得多,包括零)。 p>

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-06-07
      • 2013-02-02
      • 1970-01-01
      • 2019-04-08
      • 2012-01-01
      • 2013-02-03
      • 2019-06-03
      • 2017-06-08
      相关资源
      最近更新 更多