二維幾何-點集直徑

點集直徑,即給定點之間的最大距離。兩兩枚舉的方法需要O(n^2)時間,並不是很優秀。

有一個辦法可以更快的求出點集的直徑。首先求點集的凸包,則最大距離一定來自於凸包上的兩個頂點。由於凸包上的點的個數往往比原始點少很多,就算還是兩兩枚舉,速度也比直接枚舉快很多,當然最壞情況下時間複雜度仍是O(n^2),需要繼續改進。

假設我們已經找到了直徑,端點爲Pi和Pj,現在我們分別從Pi和Pj出發各作一條垂直於PiPj的直線。

可以證明,整個凸包都被夾在了這兩條直線中間。如果不然,不妨設Pi的下方有一個凸包的點P‘,則連接P’Pj,假設和下面那條直線交於Q,則P‘Pj>PjQ>PiPj 與 PiPj是直徑矛盾,爲了方便,我們把兩條直線看成是有向直線,使得凸包位於兩條直線的左側。

像PiPj這樣存在兩條分別穿過這兩個點的平行直線,把凸包夾在中間的點被稱爲對踵點對。給定一個角,有無數條以它爲傾角的平行直線,但其中只有兩條能把凸包緊緊的夾在中間,因此對踵點最多隻有四對(一邊兩個點),這樣,我們可以用一種稱爲旋轉卡殼的方式找出所有對踵點對。

初始時,有兩條有向直線把凸包夾在中間,一條水平向右,一條水平向左,這是可以求出初始對踵點爲Pi和Pj(就是y座標最小和最大點)。接下來,逆時針旋轉兩條直線,看看對踵點對是如何變化的。假設穿過Pi的有向直線還需要逆時針旋轉\Thetai角度才能貼住邊PiPi+1,類似定義\Thetaj。則當旋轉的角度同時小於\Thetai和\Thetaj時對踵點始終不變。

如果\Thetai<\Thetaj,當旋轉角度等於\Thetai時,穿過pi的直線邊會貼住PiPi+1,對踵點對中的Pi會變成Pi+1。同理,如果\Thetai>\Thetaj,當旋轉角度等於\Thetaj時穿過Pj的直線會貼住邊PjPj+1,對踵點對中的Pj會變成Pj+1。如果\Thetai=\Thetaj,則兩條直線同時貼住新的邊,因此Pi和Pj分別變成Pi+1和Pj+1。注意Pi和Pj+1、Pi+1和Pj是對踵點對,不要漏掉它們。重複這一過程,直到穿過Pi的直線傾角大於180度時終止。由於每次旋轉至少會有一條直線貼出新的邊,旋轉過程的時間複雜度爲O(n)

既然最遠點對只會在對踵點中取到,只需在上述過程中每找到一對新的對踵點對時更新答案,就可以求出最遠點對。

注意,上述算法知識一個概念描述,下面的代碼是實用的寫法。

首先給出輔助函數:

兩點距離的平方.

int Dist(Point a, Point b)
{
    return (a.x-b.x) * (a.x-b.x) + (a.y-b.y) * (a.y-b.y);
}

求點集直徑,返回點集直徑的平方

int diameter(Point *p, int n)
{
    int u, v, ans;
    double diff;

    p[n] = p[0];    //不用取模了
    ans = 0;        //最大距離的平方
    for(u = 0, v = 1; u < n; u++)
        //一條直線貼住p[u]-p[u+1]
        for(;;)
        {
            /*
            當Area(p[u],p[u+1],p[v+1]) <= Area(p[u],p[u+1],p[v])時停止旋轉
            即Cross(p[u+1]-p[u], p[v+1]-p[u]) - Cross(p[u+1]-p[u], p[v]-p[u]) <= 0
            而根據Cross(A,B)-Cross(A,C) = Cross(A,B-C) 得
            Cross(p[u+1]-p[u],p[v+1]-p[v]) <= 0
            */
            diff = Cross(p[u+1]-p[u], p[v+1]-p[v]);
            if(diff <= 0)
            {
                ans = max(ans, Dist(p[u], p[v]));         //u和v是對踵點
                if(diff == 0)                             //u和v+1也是對踵點b
                    ans = max(ans, Dist(p[u], p[v+1]));
                break;
            }
            v = (v+1) % n;                                //面積峯值也隨着u的移動而移動
        }

    return ans;
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章