凸包問題: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;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章