首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >convexHull实现

convexHull实现

作者头像
狼啸风云
修改2022-09-02 13:17:28
修改2022-09-02 13:17:28
7400
举报

convexHull介绍

凸包(Convex Hull)问题表示如下: 在一个实数向量空间V中,对于给定集合X,所有包含X的凸集的交集S被称为X的凸包。X的凸包可以用X内所有点(X1,…Xn)的凸组合来构造。 比较常用,也是实验中遇到的是二维平面上的凸包:给定平面上一个点集,凸包就是将最外围的点连接起来构成的凸多边形,它能包含点集中所有的点。可以想象成一条刚好包着所有点的橡皮圈。

注:这里对于边界的处理方法是舍弃位于边界(不包括顶点)上的点,如上图中的点9我们这里舍去,它不属于凸包,但它被凸包包围。

思路

首先选取横坐标最小的一个点,若有多个这样的点,选纵坐标最小(上图的红点)。然后对其余点进行遍历,以红点为极点,y轴正半轴方向为极轴,按照顺时针方向,计算其余点的角度。选取角度最小的点,加入一个点集。若有多个点角度相同,则计算红点到这些点的距离,选取距离最小的点加入该点集(9、10号点选10号),重复上述步骤,直到即将加入凸包的点为红点时结束循环,此时得到的点集即为凸包。

代码

代码语言:javascript
复制
// 计算转向角度,以y轴正向为极坐标顺时针转动,角度限制在0~360°之间
public static double calculateBearingToPoint(double currentBearing, int currentX, int currentY,
                                                 int targetX, int targetY) {
        double X = targetX - currentX, Y = targetY - currentY , targetAngle ;
        
        if( X == 0 ) {
            if( Y >= 0 )
                targetAngle = - currentBearing;
            else
                targetAngle =  180 - currentBearing;
        }
        else {
            if( X > 0 ) {
                if( Y >= 0 )
                    targetAngle = 90 - currentBearing - 180 * Math.atan( Y/X ) / Math.PI ;
                else
                    targetAngle = 90 - currentBearing - ( 360 + 180 * Math.atan( Y/X ) / Math.PI );
            }
            else {
                targetAngle = 90 - currentBearing - ( 180 + 180 * Math.atan( Y/X ) / Math.PI );
            }
        }
        
        if( targetAngle - 360.0 >= 0 )
            return targetAngle - 360.0;
        else {
            if( targetAngle + 360.0 <= 0 )
                return targetAngle + 720.0;
            else {
                if ( targetAngle < 0 )
                    return targetAngle + 360.0;
                else
                    return targetAngle;
                }
            }
    }


convexHull// 主函数
public static Set<Point> convexHull(Set<Point> point) {
        
        Iterator<Point> it = point.iterator() ;
        HashSet<Point> HullHashSet = new HashSet<>();
        
        if(point.isEmpty())  {                    //点集为空,返回空集
            return HullHashSet ;
        }
        
        Point first_point = it.next();            //找到最左点
        while(it.hasNext()) {
            Point pr = it.next() ;
            if (pr.x() < first_point.x() ) {
                first_point = pr ;
            }
        }
        
        HullHashSet.add(first_point);            //凸包中加入最左点
        point.remove(first_point);                //剩余点集中删除最左点
        
        Point this_point = first_point;            //当前点
        Point next_point;                        //下一个点
        Point add_point = this_point;            //加入凸包中的点   
        double min_angle;                        //最小转动角度
        double next_angle;                        //当前转动角度
        
        do {
            it = point.iterator();
            min_angle = 360;
            
            while(it.hasNext()) {
                next_point = it.next();
                next_angle = calculateBearingToPoint(0 , (int)this_point.x() , (int)this_point.y() , (int)next_point.x() ,(int) next_point.y() );
                if(next_angle < min_angle) {    //选取角度最小的点
                    add_point = next_point;
                    min_angle = next_angle;    
                }
                else if( next_angle == min_angle ) { //若角度相同,选取距离最远的点
                    if(Math.pow((this_point.x()-next_point.x()), 2) + Math.pow((this_point.y()-next_point.y()), 2) >
                        Math.pow((this_point.x()-add_point.x()), 2) + Math.pow((this_point.y()-add_point.y()), 2)) {
                        add_point = next_point ;
                    }
                }
            }
            
            this_point = add_point;
            HullHashSet.add(add_point);    //加入凸包
            point.remove(add_point);    //从剩余点集中删除
            point.add(first_point);        //加入初始点,否则没有循环结束条件
            
        }while(add_point != first_point);
     
        point.addAll(HullHashSet);
        return HullHashSet ;
    }
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021/05/07 ,如有侵权请联系 cloudcommunity@tencent.com 删除
目录
  • 思路
  • 代码
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档