最近软件构造(Software Construction)课程实验涉及到了求解凸包问题(Convex Hull)的算法,具体实验来源是借鉴于MIT的软构实验6.031,实验里介绍了gif-wrapping卷包裹算法,并说不限于这个算法,所以我查阅学习了其他算法来实现。MIT这个实验的官方网页是:
http://web.mit.edu/6.031/www/fa18/psets/ps0/
你可以从这里下载实验包 https://github.com/hit-ls/MIT-6.031
对凸包引用MIT的解释就是
It’s a minimal subset of the input points that form the vertices of the perimeter of the convex hull.The smallest convex set contains all the points in a set of input points.
即有一系列二维平面中的点构成集合,求凸包就是找到这个点集的某一子集,使得子集里的点按顺时针或逆时针顺序连接后构成一个将整个点集包围的封闭多边形,且该子集的阶数要最小。下面是我画的一个草图,“在二维欧几里得空间中,凸包可想象为一条刚好包着所有点的橡皮圈”。
对点Point类的定义如下
/* Copyright (c) 2007-2016 MIT 6.005 course staff, all rights reserved.
* Redistribution of original or derived work requires permission of course staff.
*/
package turtle;
/**
* An immutable point in floating-point pixel space.
*/
public class Point implements Comparable<Point>{
private final double x;
private final double y;
/**
* Construct a point at the given coordinates.
* @param x x-coordinate
* @param y y-coordinate
*/
public Point(double x, double y) {
this.x = x;
this.y = y;
}
/**
* @return x-coordinate of the point
*/
public double x() {
return x;
}
/**
* @return y-coordinate of the point
*/
public double y() {
return y;
}
@Override //重写compareTo方法方便后续排序
public int compareTo(Point o) {
double i = this.x() - o.x();//先按照x排序
if(i == 0){
return (int) (this.y - o.y());//如果x相等了再用y进行排序
}
return (int) i;
}
}
接下来我们开始思考如何从一堆无序的点中提取出期望的点集。
就像在人群中分辨男人女人一样,我们可以看他有没有喉结,肩比臀宽或是窄等等来区别。我们从一堆事物中寻找想要的某一类,首先要提取总结出期望事物的特性,然后再根据他的这一特性去一个个套用是否符合。安德鲁算法用以判断的依据便是凸包点集中,若取任意连续的三个点A,B,C,如果他们按顺时针顺序排列,则向量AB x AC的代数值小于0,逆时针排列则大于0。下面画图解释。
如图,A、B、C是凸包点集按顺指针排序的三个连续点,用右手法则就能简单地判断出AB x AC是指向平面内的,也就是小于0。那么为什么会小于0呢,稍加想象就能知道,如果我们按顺时针依次走完整个凸包,那每到一个点我们的面朝向就会顺时针旋转来调整得到下一个方向,并且调整的度数总和还是360°。那么C点一定在AB连线的顺时针方向,图中已经标明,也使得