1. 程式人生 > >計算幾何旋轉卡殼求凸包直接原理詳解

計算幾何旋轉卡殼求凸包直接原理詳解

先提一下最基本最暴力的求凸包直徑的方法吧—列舉。。。好吧。。很多問題都可以用 列舉 這個“萬能”的方法來解決,過程很簡單方便是肯定的,不過在效率上就要差很遠了。 要求一個點集的直徑,即使先計算出這個點集的凸包,然後再列舉凸包上的點對,這樣來求點集直徑的話依然會在凸包上點的數量達到O(n)級別是極大的降低它的效率,也浪費了凸包的優美性質。不過在資料量較小或者很適合時,何必要大費周折的用那些麻煩複雜的演算法呢,列舉依然是解決問題的很好的方法之一。

然後就是今天的旋轉卡殼演算法了。

旋轉卡殼可以用於求凸包的直徑、寬度,兩個不相交凸包間的最大距離和最小距離等。雖然演算法的思想不難理解,但是實現起來真的很容易讓人“卡殼”。

其實簡單來說就是用一對平行線“卡”住凸包進行旋轉。

被一對卡殼正好卡住的對應點對稱為對踵點,對鍾點的具體定義不好說,不過從圖上還是比較好理解的。

可以證明對踵點的個數不超過3N/2個 也就是說對踵點的個數是O(N)的

對踵點的個數也是我們下面解決問題時間複雜度的保證。

卡殼呢,具體來說有兩種情況:

1.

一種是這樣,兩個平行線正好卡著兩個點;

2.

一種是這樣,分別卡著一條邊和一個點。

而第二種情況在實現中比較容易處理,這裡就只研究第二種情況。

在第二種情況中 我們可以看到 一個對踵點和對應邊之間的距離比其他點要大(借用某大神的圖··)

也就是一個對踵點和對應邊所形成的三角形是最大的 下面我們會據此得到對踵點的簡化求法。

下面給出一個官方的說明:

Compute the polygon’s extreme points in the y direction. Call them ymin and ymax. Construct two horizontal lines of support through ymin and ymax. Since this is already an anti-podal pair, compute the distance, and keep as maximum. Rotate the lines until one is flush with an edge of the polygon. A new anti-podal pair is determined. Compute the new distance, compare to old maximum, and update if necessary. Repeat steps 3 and 4 until the anti-podal pair considered is (ymin,ymax) again. Output the pair(s) determining the maximum as the diameter pair(s).

要是真的按這個實現起來就麻煩到吐了。。

根據上面的第二種情況,我們可以得到下面的方法:

如果qa,qb是凸包上最遠兩點,必然可以分別過qa,qb畫出一對平行線。通過旋轉這對平行線,我們可以讓它和凸包上的一條邊重合,如圖中藍色直線,可以注意到,qa是凸包上離p和qb所在直線最遠的點。於是我們的思路就是列舉凸包上的所有邊,對每一條邊找出凸包上離該邊最遠的頂點,計算這個頂點到該邊兩個端點的距離,並記錄最大的值。

直觀上這是一個O(n2)的演算法,和直接列舉任意兩個頂點一樣了。

然而我們可以發現 凸包上的點依次與對應邊產生的距離成單峰函式(如下圖:)

這個性質就很重要啦。

根據這個凸包的特性,我們注意到逆時針列舉邊的時候,最遠點的變化也是逆時針的,這樣就可以不用從頭計算最遠點,而可以緊接著上一次的最遠點繼續計算。於是我們得到了O(n)的演算法。這就是所謂的“旋轉”吧!

利用旋轉卡殼,我們可以在O(n)的時間內得到凸包的對鍾點中的長度最長的點對。

又由於最遠點對必然屬於對踵點對集合 ,那麼我們先求出凸包 然後求出對踵點對集合 然後選出距離最大的即可

程式碼

struct Point
{
    double x,y;
};
double cross(Point p1,Point p2,Point p3)
{
    return (p2-p1)^(p3-p1);
}
double  rotating_calipers(Point p[],int n)
{
    int now=1;
    p[n]=p[0];
    double ans=0;
    for(int i=0;i<n;i++)
    {
        while(cross(p[i],p[i+1],p[now+1])>cross(p[i],p[i+1],p[now]))
        {
            now=(now+1)%n;
        }
        ans=max(ans,max(dist(p[i],p[now]),dist[p[i+1],p[npw+1]]));
    }
    return ans;
}   

參考部落格