首页 文章

划分和征服最近对算法

提问于
浏览
-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;
        }
    }

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

  • 0

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

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


    在"4 or more points"情况下, minDisDivideConquer() 方法的另一个可能的改进是防止查看已在递归调用中考虑的对 .

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

    但是,没有必要查看 mid 之前或 mid 之后的两个点(因为这些对已经在递归调用中被考虑过) .

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

相关问题