凸包问题的gift-wrapped算法

目录

1 凸包的定义

  1.1 顶点定义法

  1.2 其他定义方法

2 Gift-wrapped算法

   2.1 算法的思想

   2.2 程序流程图 

3 算法的实现(Java)

   3.1 案例分析

   3.2 代码


1 凸包的定义

凸包可分为二维凸包(Polygon)与三维凸包(Polyhedron),本文主要介绍二维凸包。

1.1 顶点定义法

给定R^{n}空间上的有限点集S,由点集中有限个极点(Extreme Point)构成的凸对象(Convex Object),就称为凸多胞体。

极点可看成是多胞体的顶点,以二维多边形为例,极点就相当于凸多边形的顶点。使用严格的数学定义来说,极点是指那些不能由凸多胞体内的其它点的凸组合(Convex Combination)表示出来的点。给定点x_{1},...,x_{k}一共k个点它的凸组合可以用下面的等式表示:

                                                            a_{1}x_{1}+...+a_{_k}x_{_k} \geq 0, a_{_1}+...+a_{_k} = 1

一条线段就是它的端点的所有的凸组合,三角形就是它的三个顶点的凸组合,一个四面体是它的四个顶点的凸组合。

换句话,对于R^{n}空间中的对象K,如果对象中的任意两个点构成的线段,也包含在对象K内,则称对象K是凸的。如下图所示的两个多边形P1和P2,多边形P1中的任意两点连成的线段也在多边形内,因此多边形P1是凸的;但是多边形P2中,存在两点的连成的线段不存多边形内的情况,如图中的线段A_{_2}B_{_2},因此多边形P2不是凸的。

                                                       

图1. 多边形P1、P2

1.2 其他定义方法

其它定义方法还包括:半空间半定义法、面(Facet)的定义、岭(Ridge)的定义等,本文主要介绍顶点定义法。

 

2 Gift-wrapped算法

2.1 算法的思想

  1. 找一个极点(左上角、左下角、右上角、右下角);
  2. 遍历所有点,找出和极点偏转角度最小的点(顺时针),当偏转角度相同时找与极点距离更远的点 ;
  3. 将当前点作为下一个极点,继续找点 ;
  4. 直到找到第一个极点,算法结束 。

2.2 程序流程图

                                     

图2. gift-wrapped算法流程图

 

3 算法的实现(Java)

3.1 案例分析

考虑依次输入的五个点(1,1)、(10,10)、(1,10)、(1,2)、(2,3)、(3,2),要求计算构成凸包周长顶点的输入点的最小子集。

 

对于这个问题,结合前面的算法思想,我们需要先完成一个计算与极点转动角度的函数calculateBearingtoPoint(double currentbearing, int currentX, int currentY, int targetX, int targetY)。

这个函数的思想就是:首先依据给定的起始点、目标点的坐标分坐标轴的四个象限来讨论计算出它们连线与north方向的夹角x(此时假设currentbearing为0),最后分两种情况:1.夹角角度大于起始角度:转动角度就等于夹角与起始角度的差值;2.夹角角度小于起始角度:转动角度就等于360.0与起始角度的差值再加上x。依据此原理,设计的calculateBearingToPoint函数:

public static double calculateBearingToPoint(double currentBearing, int currentX, int currentY, int targetX, int targetY) {
        double x=0,t1=0,t2=0,angle=0;
        t1 = (double) targetX - currentX;
        t2 = (double) targetY - currentY;
        angle = Math.toDegrees(Math.atan(t1 / t2));
        if(t2 == 0)
            x = t1 > 0 ? 90 : 270;
        else if((t1>=0 && t2>0) || (t1<0 && t2!=0))
            x = angle;
        else
            x = 270 - angle;
        if(x >= currentBearing)
            return x - currentBearing;
        else
            return 360 - currentBearing + x;
    }

然后结合前面的gift-wrapped算法的分析,完成convexHull(Set<Point> points)函数。

3.2 代码

public static double calculateBearingToPoint(double currentBearing, int currentX, int currentY, int targetX, int targetY) {
        double x=0,t1=0,t2=0,angle=0;
        t1 = (double) targetX - currentX;
        t2 = (double) targetY - currentY;
        angle = Math.toDegrees(Math.atan(t1 / t2));
        if(t2 == 0)
            x = t1 > 0 ? 90 : 270;
        else if((t1>=0 && t2>0) || (t1<0 && t2!=0))
            x = angle;
        else
            x = 270 - angle;
        if(x >= currentBearing)
            return x - currentBearing;
        else
            return 360 - currentBearing + x;
    }

 public static Set<Point> convexHull(Set<Point> points) {
        //throw new RuntimeException("implement me!");
        ArrayList<Point> cH = new ArrayList<Point>();
        ArrayList<Point> scH = new ArrayList<Point>();
        scH.addAll(points);
        int length = scH.size();
        Point p;
        if(length > 3)
        {
               p = scH.get(0);
               for(int i=1; i<length; i++) //首先确定左下角的点为极点
               {
                   if(p.x() > scH.get(i).x())
                       p = scH.get(i);
                   else if(p.x() == scH.get(i).x() && p.y() > scH.get(i).y())
                       p = scH.get(i);
               }
               cH.add(p); // 将第一个极点加入凸包
               Point current=p;
               double smallangle,smalldis;
               int i=0;
               scH.remove(current);
               while(true)
               {
                   double currentbearing = 360.0, dis=0.0;
                   Point point = null;
                   for (Point target : scH)
                   {
                       smallangle = calculateBearingToPoint(0, (int) current.x(), (int) current.y(),(int) target.x(), (int) target.y());
                       smalldis = Math.pow(current.x() - target.x(), 2) + Math.pow(current.y() - target.y(), 2);
                       if (smallangle < currentbearing)
                       {
                           point = target;
                           currentbearing = smallangle;
                           dis = smalldis;
                       }
                       else if (smallangle == currentbearing && smalldis > dis) //偏角相同,取距离远的
                       {
                           point = target;
                           dis = smalldis;
                       }
                   }
                   current = point;
                   cH.add(point);
                   scH.remove(point);
                   i++;
                   if(i == 1)
                       scH.add(p);
                   if(cH.get(i) == p) //若再次遇到初始极点,则算法终止
                       break;
               } 
               Set<Point> res = new HashSet<Point>();
               res.addAll(cH);
               return res;
        }
        else
            return points;
    }

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值