目录
1 凸包的定义
1.1 顶点定义法
1.2 其他定义方法
2 Gift-wrapped算法
2.1 算法的思想
2.2 程序流程图
3 算法的实现(Java)
3.1 案例分析
3.2 代码
1 凸包的定义
凸包可分为二维凸包(Polygon)与三维凸包(Polyhedron),本文主要介绍二维凸包。
1.1 顶点定义法
给定空间上的有限点集S,由点集中有限个极点(Extreme Point)构成的凸对象(Convex Object),就称为凸多胞体。
极点可看成是多胞体的顶点,以二维多边形为例,极点就相当于凸多边形的顶点。使用严格的数学定义来说,极点是指那些不能由凸多胞体内的其它点的凸组合(Convex Combination)表示出来的点。给定点,...,
一共k个点它的凸组合可以用下面的等式表示:
一条线段就是它的端点的所有的凸组合,三角形就是它的三个顶点的凸组合,一个四面体是它的四个顶点的凸组合。
换句话,对于空间中的对象K,如果对象中的任意两个点构成的线段,也包含在对象K内,则称对象K是凸的。如下图所示的两个多边形P1和P2,多边形P1中的任意两点连成的线段也在多边形内,因此多边形P1是凸的;但是多边形P2中,存在两点的连成的线段不存多边形内的情况,如图中的线段
,因此多边形P2不是凸的。
![](https://img-blog.csdnimg.cn/20210522163639652.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1ZlcnN1czY2Ng==,size_16,color_FFFFFF,t_70)
1.2 其他定义方法
其它定义方法还包括:半空间半定义法、面(Facet)的定义、岭(Ridge)的定义等,本文主要介绍顶点定义法。
2 Gift-wrapped算法
2.1 算法的思想
- 找一个极点(左上角、左下角、右上角、右下角);
- 遍历所有点,找出和极点偏转角度最小的点(顺时针),当偏转角度相同时找与极点距离更远的点 ;
- 将当前点作为下一个极点,继续找点 ;
- 直到找到第一个极点,算法结束 。
2.2 程序流程图
![](https://img-blog.csdnimg.cn/20210522221808429.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1ZlcnN1czY2Ng==,size_16,color_FFFFFF,t_70)
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;
}