凸包问题:Gift Wrapping礼品包装算法

凸包问题:Gift Wrapping算法

凸包问题

凸包(Convex Hull)是一个计算几何(图形学)中的概念。简而言之,就是给定平面点集,找出该点集中最外圈的点构成凸多边形,使得该平面点集中所有的点都在该凸多边形内或该多边形边上。
正如上图所示,红色圈圈部分就是该平面点集的凸包。

凸包问题算法

凸包问题的解法一般有如下几种:
穷举法(蛮力法) O(n3)
分治法 O(nlogn)
Graham扫描法 O(nlogn)
jarvis步进法 O(nH)(H是凸包上点的个数)
Gift Wrapping礼品包装算法 O(nlogn)
关于前五种算法,下面这个博客已经写的很清晰了:
https://blog.csdn.net/baningtao1470/article/details/101077108?utm_source=app
我再记录一下Gift Wrapping算法。

Gift Wrapping算法

基本思路:
先确定一个肯定在凸包上的点P0,然后由此出发寻找下一个凸包上的点P1,直到确定凸包上的点是P0为止。
可以把这个过程形象化的理解为小时候往钉子板上圈皮筋的过程,先把皮筋固定在一个钉子上,然后再绕一个钉子,直到最后把皮筋绑在最开始固定的那个钉子上,也就形成了一个圈。
第一:怎么确定一个必在凸包上的点?
可以想象,在该平面点集中最远的点必在凸包上。怎么去想这个最远,可以是最上,可以是最下,也可以是最左,也可以是最右,我的实现过程选择的是最左下,只需要对平面点集遍历一遍,找到横座标最小的那个点,如果横座标相等,那么就选择纵座标较小的即可。
第二,怎么确定由已知点确定下一个点?
可以利用极角,也可以利用向量的叉乘来做。我们去观察下一个点的特点:假如以已知点为极座标原点,那么下一个点则是极角最小的那个点。
接下来考虑怎么求极角,这就不细说了,高中数学知识,下面有代码实现,假设当前点(x1,y1),考察点(x2,y2),求cos,求sin,求tan,最后在用arc就能知道角度,我用的是Matn.atan(),要注意的是记得根据点所在的象限分类讨论。
第三,如果由共线的情况,那么这些共线的点的极角相同,该怎么处理?
这要看你需要求的是最小凸包还是最大凸包了,如果是最小凸包,那么就选这些点中与已知点距离最大的那一个;如果是最大凸包,那么就选与已知点距离最小的那一个。我在实现的时候,用了一个动态栈来保存可供选择的点:如果这个找到了一个角度更小的点,清空栈中所有的内容,再将该点放入栈顶;如果角度相等,那么直接入栈。这样如果最后,栈中只有一个元素,那么就是下一个凸包中的点;如果栈中有多个,则这些点共线,再比较距离即可。我觉得这样还挺好


    /*Calculate angle 
    a:definite point
    b:investigated point
    if this point is definite point,return -1,as a sign
    */
    public static double calculateConvexHullAngle(Point a,Point b) {
     double cosSign = -2;
     double sinSign = -2;
     double angle = 0;
  cosSign = b.x()-a.x();
  sinSign = b.y()-a.y(); 
  if(cosSign>0 && sinSign>=0)
   angle = Math.toDegrees(Math.atan(sinSign/cosSign));
  else if((cosSign<=0 && sinSign>0)||(cosSign<0 && sinSign<=0))
   angle = Math.toDegrees(Math.atan(sinSign/cosSign))+180;
  else if(cosSign>0 && sinSign<0)
   angle = Math.toDegrees(Math.atan(sinSign/cosSign))+360;
  else if(cosSign==0 && sinSign==0) 
   angle = -1;
  return angle;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章