graham scan演算法說明了凸包構造問題的下界o(nlogn)是可以達到的。其實o(nlogn)的演算法遠不止這一種,分治法就是一種能達到o(nlogn)複雜度的思想。在此引入運用分治思想的兩種演算法來構造凸包。
引入新演算法之前依舊先來回顧乙個經典排序演算法:歸併排序(merge sort)。歸併排序的基本流程如下:
演算法分為兩個階段:分(divide)和歸併(merge)。分的階段將待排序列均分到乙個個子序列(如圖中劃分到單個元素)。歸併階段將分好的子串行兩兩合併成有序序列,重複合併的過程直到整體歸為乙個序列。歸併過程共logn步,每步耗費n的時間,總體複雜度為o(nlogn)。
歸併排序的過程就是乙個典型的分治(divide-and-conquer)策略。凸包構造問題也可以套用這種策略來分而治之,逐步求解。我們可以將待處理點集s分為同等規模的兩個子點集,並分別對其求凸包。
有了兩個子解後,問題就變成了如何適當加一些邊,將兩個子凸包merge成整體解。分治法核心的任務就是如何merge。
接下來的分析不考慮退化情況,比如三點共線等特殊情況的處理。
分治法解決問題的過程可以概括為:大事化小,小事化了。就是首先將問題劃分為易求解的子問題,子問題套用已知方法解答即可。例如子凸包的構造就能用graham scan來解決。
graham scan解決問題的前提是:參照基準點,其他點按極角有序排列,也就是構成了乙個有序的星形多邊形(star-shaped polygon)。首先要做的就是將兩個子凸包預處理成兩個star-shaped polygon。
由於任何乙個凸多邊形都是star-shaped polygon,它必然有乙個核,其他點按極角有序排列。問題在於如何找到乙個公共核,使得兩個子凸包同時關於這個核是極角有序排列的。也就是公共核處於兩個凸包的交部分,這樣是最好處理的情況(如下左圖)。不過還有可能有其他情況,不能找到公共核(如下中圖),甚至兩個凸包根本不相交(如下右圖):
這就要將分治策略分不同情況來實現。首先看最簡單的情況,兩個子凸包有公共核。
相對於公共核,兩個子凸包的各自有序排列,相互交錯。要做的就是將二者點序列合併,方法正是典型的二路歸併,線性時間可以完成。最後進行graham scan即可得到大凸包。
存在公共核的情況處理是很簡單的,再看第乙個子凸包的核落在第二個子凸包外部的情況。如下圖所示:
這中情況與增量構造法的情況很相似,p1的核x相對於p2就是乙個新加入的點。做出兩條support line:x→t和x→s,捨棄p2上t→s路徑的點即可。這樣p2中剩餘點與x構成了乙個星形多邊形,x也成為了p2的核。這就轉化成了第一種有公共核的情況。
上述分治策略的演算法過於複雜,所以引入一種更加簡明的分治策略。這種分治策略也會為三角剖分等問題提供思路。
首先規定一種點集劃分的策略。假設待合併的兩個子凸包是沿著某方向是分離的,二者不想交。例如下圖凸包p1和p2就是相互分離的:
這樣劃分會使得合併更加簡明,不必區分多種複雜情況。為了滿足這種劃分策略,引入一種預處理,也就是乙個x方向的排序過程(x-sorting)。排序後就可取點x座標的中值,將點集劃分為規模相當的左右兩個子集。每個凸包都有其最左點l和最右點r,如上圖。
merge操作就是將兩個左右相離的兩個子凸包合併為乙個大凸包的過程了。運算的關注的正是兩對l和r點。
先直觀感受一下merge操作要新增的新極邊:
上下兩條紫色邊正是要求的新邊,又稱支撐邊(support line),並且每次merge只會增加兩條新邊。兩條邊類似兩個圓的公切線(common tangent),將二者連線起來。
直觀上感覺,兩條support line正是兩個子凸包的最高點t和最低點b相互連線得到的,這些點只需線性時間就能找到。當真如此的話凸包構造的下界就成了o(n),顯然直覺是錯誤的。例如下面的兩種情況,support line就和b、t兩點沒有直接關係了:
構造support line的過程需要縝密的分析,並非憑直覺能得到的。
構造過程首先從左凸包的r點和右凸包的l點連線開始,以這條線為基礎逐步得到support line。
注意乙個細節問題:如何得到各子凸包的l點和r點。每次合併都會產生新的凸包,所以凸包是乙個動態的結構。當然可以每次計算出最左點和最右點,只需要線性時間。但是這並不是最優的方式。考慮分治的思想,就整個merge流程來講,是自底向上將子凸包兩兩合併的過程。因此只要在最底層上最小的子凸包中記錄最左點和最右點,每次merge更新一下這兩個變數即可,只需要o(1)的常數時間!這種優化對整體的複雜度上線nlogn雖然沒有影響,也能為程式節省一部分的開銷。
再看如何將最初的r-l線變成support line,在此以upper support line為例。演算法的核心依然是to left test。
觀察support line的兩個端點的特徵,可以看出他們對於前驅後繼都是rr或ll型的,也就是前後相鄰的點都落在support line的同側。其餘任兩點的連線都只能是lr或rl的,這也是增量構造演算法的核心思想。兩次to left 測試即可得出型別。
從r-l線出發,首先看l點,可以發現其前驅後繼相對於r→l是rl型的。若要得到rr型的特徵,必須向其後繼方向推進乙個單位。
將l點向其後繼推進一單位,更新了r-l線。判定l點依舊是rl型的,r-l線還不是support line,繼續推進l點。判定新的r-l線,l點的特徵變為了rr型,暫時可以認為l點滿足要求,接下來考察r點。
r點的前驅後繼對於線l→r是rl型的,若要得到ll的特徵,必須向其前驅方向推進乙個單位。以此往復,直到r點特徵變為ll。
然後回頭考察l點,發現此時l點已經不滿足rr了,變回了rl。繼續同樣的步驟推進l點直到特徵變為rr。然後在回頭考察r點,發現它依舊滿足ll,演算法停止。至此r和l兩點連線就是upper support line了,同樣lower support line構造方式類似。
回顧由r-l線逐步推進得到support line的過程,每次操作乙個端點,得到是一種「z」字形(zig-zag)的推進軌跡。操作點的切換由另一點滿足要求決定,而演算法停止的依據是兩個端點同時滿足了要求。這種方式類似快速排序構造軸點的過程,左右兩軸點交替操作,直到二者都滿足要求時演算法停止。
如此通過兩次「zig-zag」的過程就能得到兩條support line,完成兩個子凸包的合併。
分析一下演算法時間複雜度。演算法首先要按照x座標排序,排序複雜度為o(nlogn)。再看merge過程,無論是左側子凸包還是右側子凸包,對於其每個點的操作至多只有以此,也就是每次歸併是線性時間。歸併共logn次,演算法的總體複雜度就是o(nlogn)了。
最後總結一下第二種分治法的特點。此前jarvis march演算法雖然以平方複雜度為上界,但其」輸出敏感性「使得實際複雜度為o(hn),最好情況下僅甚至為線性。例如如下情況:
jarvis march演算法的複雜度變為了o(4n),而此種分治法依舊會經歷按部就班的x-sorting,一上來就注定了o(nlogn)的複雜度,然後經歷同樣o(nlogn)的merge過程。也就是說這種分治法在各種情況下的表現都是很均勻的。
計算幾何入門 1 3 凸包的構造 增量構造法
極點法和極邊法的複雜度分別為o n 4 和o n 3 當點集s的規模稍大時就難以適用了。為了滿足實際需要必須尋找更高效的演算法來構造凸包。在引入新演算法之前首先來回顧一下經典的演算法思想 減治 decrease and conquer 注意不是分治 divided and conquer 二者稍有區...
計算幾何凸包入門詳解
講這個之前,先說一下我自己的看法 求凸包網上有很多種方法,個人覺得最好用最常用的就是graham掃瞄法,本篇博文我也就只講這一種演算法求解凸包。講這個問題之前我們必須要弄清楚何為凸包?我來口胡一下吧,很明顯凸包這個名詞就已經給了我們乙個重要的資訊,那就是這個東西它肯定是乙個凸多邊形。那除了是凸多邊形...
計算幾何 凸包
有多個手機訊號發射器,求解乙個最小區域,要求所有的發射器都包含在這個最小區域中,並且任意兩台發射器之間的交流包含於在這個區域內。將所有的發射器看做為點,任意兩個點之間的連線都包含於乙個平面s 乙個平面的子集s 是凸的,當且僅當 s中的任意兩個點之間的連線都包含於 s中。點集 p的凸包是所有包含 p的...