平面点对实验

OJ地址:POJ3714    ZOJ2107     HDU1007

参考:点击打开链接

本人的实验程序:

package sse.algorithm;

import java.util.ArrayList;
import java.util.List;

public class ClosestPointPair {
	/*
	 * 实验:分治法O(n*lg(n))时间内求二维点集最近点对算法
	 * 作者:xxxxxxx098
	 * 时间:2012-12-07 19:00
	 */
    
    /*
     * boolean Cpair2(S,d)算法步骤:
     * n = |S|;
     * if(n < 2) return false;
     * 将点集 S 中的点依其 y 坐标值排序,然后作以下事情
     * 1.  m = S中各点x横坐标的中位数
     *     构造S1,S2
     *     S1 = {p in S| x(p) <= m}, S2 = {p in S| x(p) > m}
     * 2.  Cpair2(S1,d1) 
     *     Cpair2(S2,d2)   
     * 3.  dm = min(d1,d2)
     * 4.  设P1是S1中距离垂直分割线l的距离在 dm 之内的所有组成的集合,
     *     P2是S2中距离垂直分割线l的距离在 dm 之内的所有点组成的集合,
     * 5.  通过扫描 P1 以及对于 P1 中每个点检查 P2 中与其距离在 dm 之内的所有点(最多6个)可以完成合并
     *     当 P1中 扫描指针逐次向上移动时,P2中的扫描指针可在宽为 2dm 的一个区间内移动
     *     设dl是按这种扫描方式找到的点对间的最小距离
     * 6.  d = min(dm,dl)
     *     return true;
     *       
     */
    
    public Point p1;//最近点对中p1的坐标
    public Point p2;//最近点对中p1的坐标
    public double d = Double.MAX_VALUE;//点对间的距离
    //public Logger log = Logger.getLogger(ClosestPointPair.class);
    
    public ClosestPointPair(){}
    
    /*
     * 返回List[start..end]中第order小的元素的x属性的值
     */
    public double find(List<Point> S,int start,int end,int order){
        if(start>end || order>end-start+1){
            return -1;
        }
        int res = partitionX(S,start,end);
        int k = res-start+1;
        if(k == order)
            return S.get(res).x;
        else if(k > order)
            return find(S,start,res-1,order);
        else
            return find(S,res+1,end,order-k);
    }
    
    // 依x 坐标值寻找划分元的位置,返回划分元在S中的下标
    public static int partitionX(List<Point> S,int low, int high){
        int randomPivot = low+ (int)(Math.random()*(high-low+1));
        double pivotValue = S.get(randomPivot).x;
        
        while(low < high){
            //从右边找比枢纽元素小的的元素
            while((low < high) && (S.get(high).x >= pivotValue)){
                high--;
            }
            //交换两个元素
            S.get(low).swapWith(S.get(high));
            //从左边找比枢纽元素大的的元素
            while((low < high) && (S.get(low).x <= pivotValue)){
                low++;
            }
            S.get(high).swapWith(S.get(low));
        }
        return low;
    }
    
    /*
     * 以下完成把点集 S 中的点依其 y 坐标值排序
     */
    public static void sortPointListByY(List<Point> S,int low,int high){
        if(low < high){
            int mid =partitionY(S,low,high);
            sortPointListByY(S,low,mid-1);
            sortPointListByY(S,mid+1,high);
        }
    }
    
    // 依y 坐标值寻找划分元的位置
    public static int partitionY(List<Point> S,int low, int high){
        int randomPivot = low+ (int)(Math.random()*(high-low+1));
        double pivotValue = S.get(randomPivot).y;
        
        while(low < high){
            //从右边找比枢纽元素小的的元素
            while((low < high) && (S.get(high).y >= pivotValue)){
                high--;
            }
            //交换两个元素
            S.get(low).swapWith(S.get(high));
            //从左边找比枢纽元素大的的元素
            while((low < high) && (S.get(low).y <= pivotValue)){
                low++;
            }
            S.get(high).swapWith(S.get(low));
        }
        return low;
    }
    /*
     * 以下求二维平面的最近点对
     * Cpair2(S1,d1) 
     * Cpair2(S2,d2)   
     * dm = min(d1,d2)
     * 设P1是S1中距离垂直分割线l的距离在 dm 之内的所有点组成的集合,
     *   P2是S2中距离垂直分割线l的距离在 dm 之内的所有点组成的集合,
     * 
     * 通过扫描 P1 以及对于 P1 中每个点检查 P2 中与其距离在 dm 之内的所有点(最多6个)可以完成合并
     * 当 P1中 扫描指针逐次向上移动时,P2中的扫描指针可在宽为 2dm 的一个区间内移动
     * 设dl是按这种扫描方式找到的点对间的最小距离
     * 
     * d = min(dm,dl)
     * 
     */
    public double Cpair2(List<Point> S) {
        
        if(S.size() < 2)return Double.MAX_VALUE;
        List<Point> S1 = new ArrayList<Point>();
        List<Point> S2 = new ArrayList<Point>();
        
        double median = find(S, 0, S.size()-1, (int)((S.size()+ 1)/2));
        
        dividePoints(S,median,S1,S2);//将点集一分为二
        
        double d1 = Cpair2(S1);
        double d2 = Cpair2(S2);
        double dm = Math.min(d1, d2);
        
        List<Point> P1 = new ArrayList<Point>();
        List<Point> P2 = new ArrayList<Point>();
        narrowToP1P2(S1,S2,median,dm,P1,P2);//缩小S1,S2的范围至P1,P2
        
        //扫描P1
        double tmp;
        Point point1 = null;
        Point point2 = null;
        for(int i=0;i<P1.size();i++){
            point1 = P1.get(i);
            //以下获得P2中的候选点,count计数最多6个
            for(int j=0,count=0;j<P2.size() && count<6;j++){
                point2 = P2.get(j);
                if(Math.abs(point1.y - point2.y) <= dm){
                    count++;
                    //逐一检查候选点
                    tmp = point1.distanceTo(point2);
                    if(tmp < this.d){
                        this.d = tmp;
                        this.p1 = point1;
                        this.p2 = point2;
                    }
                }
            }
        }
        return Math.min(dm, this.d);
    }
	
	//按S中x坐标的总位数,把S划分为S1,S2
    //其中,S1 = {p in S| x(p) <= m}, S2 = {p in S| x(p) > m}
	public void dividePoints(List<Point> S,double median,List<Point> S1,List<Point> S2){
	    for(int i=0;i<S.size();i++){
	        Point p = S.get(i);
	        if(p.x <= median)
	            S1.add(p);
	        else
	            S2.add(p);
	    }
    }
	
	//P1是S1中距离垂直分割线l的距离在 dm 之内的所有点组成的集合,
    //P2是S2中距离垂直分割线l的距离在 dm 之内的所有点组成的集合,
	public void narrowToP1P2(List<Point> S1,List<Point> S2,double median,double dm,List<Point> P1,List<Point> P2){
	    for(int i=0;i<S1.size();i++){
	        if((median - S1.get(i).x) <= dm){
	            P1.add(S1.get(i));
	        }
	    }
	    
	    for(int i=0;i<S2.size();i++){
            if((S2.get(i).x - median) <= dm){
                P2.add(S2.get(i));
            }
        }
	}
	/*
	 * 蛮力法求最近点对
	 * 用于对比验证试验的正确性
	 */
	public double rudeGet(List<Point> S){
	    if(S.size()<2)return Double.MAX_VALUE;
	    double res=Double.MAX_VALUE;
	    double tmp;
	    Point pt1,pt2;
	    for(int i=0;i<S.size()-1;i++){
	        pt1 = S.get(i);
	        for(int j=i+1;j<S.size();j++){
	            pt2 = S.get(j);
	            tmp = pt1.distanceTo(pt2);
	            if(tmp < res){
	                res = tmp;
	                this.d = tmp;
	                this.p1 = pt1;
	                this.p2 = pt2;
	            }
	        }
	    }
	    return res;
	}
	public double randomDouble(){
	    return Math.random()*1000;
	}

	public static void main(String[] args) {
	    int TOTALPOINTS = 5000;//要生成的点的总数
	    ClosestPointPair cp = new ClosestPointPair();
	    ClosestPointPair cp2 = new ClosestPointPair();
	    
	    //先随机生成0-1000内的坐标值,暂存到二维数组
	    double arraypoint[][] = new double[TOTALPOINTS][2];
	    for(int i=0;i<TOTALPOINTS;i++)
	        for(int j=0;j<2;j++)
	            arraypoint[i][j]= cp.randomDouble();
	    //考虑到点可能有很多,故将点集存到链表中
	    List<Point> S = new ArrayList<Point>();
	    List<Point> S_backup = new ArrayList<Point>();
	    for(int i=0;i<arraypoint.length;i++){
	        S.add(new Point(arraypoint[i][0],arraypoint[i][1]));
	        S_backup.add(new Point(arraypoint[i][0],arraypoint[i][1]));
	    }
	    //预处理:按y坐标排序
	    sortPointListByY(S, 0, S.size()-1);
	    sortPointListByY(S_backup, 0, S_backup.size()-1);
	    System.out.println("Total points:"+TOTALPOINTS);
	    //统计蛮力法的时间
	    long time1,time2;
	    time1=System.currentTimeMillis();
	    double res = cp.rudeGet(S);
	    time2=System.currentTimeMillis();
	    System.out.println("-----------[O(n*n)]----------------------\nclosest distance is:"
	            +res+"\nPoint Pair:"+cp.p1+","+cp.p2+"\ntime:"+(time2-time1)+" ms");
	    //统计分治法的时间
	    time1=System.currentTimeMillis();
	    res = cp2.Cpair2(S_backup);
	    time2=System.currentTimeMillis();
	    System.out.println("\n-----------[O(n*lg(n))]------------------\nclosest distance is:"
	            +res+"\nPoint Pair:"+cp2.p1+","+cp2.p2+"\ntime:"+(time2-time1)+" ms");
    }
}

class Point{
    public double x;
    public double y;
    public Point(){}
    public Point(double x,double y){
        this.x=x;
        this.y=y;
    }
    public double distanceTo(Point p){ //假设p != null
        return Math.sqrt((x-p.x)*(x-p.x) + (y-p.y)*(y-p.y));
    }
    public void swapWith(Point p){
        double tmpx=p.x, tmpy=p.y;
        p.x = this.x;
        p.y = this.y;
        this.x = tmpx;
        this.y = tmpy;
    }
    public String toString(){
        return "("+x+","+y+")";
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值