【學習筆記:CG基礎2】 Convex Hull
Ahead
10.6.2018
開始第二個算法了
篇章1
前面就不多寫了第一篇裏面的有些代碼後面還用到不重寫了
Beginning
算法2 (EE)
概念
極邊(Extremity Edge): 也就是兩邊為相鄰極點的邊(相鄰很重要!!! ) (EE)
非極邊 (Non-Extremity Edge):剩下的
分析
和之前的一樣
對於每一條極邊,也有這樣的特性,所有的點都落在同側同樣也是充要的。
這個證明和之前的差不多,想一下也沒什麽問題的(嚴謹的我也不會)
所以問題就變成了找極邊
偽代碼
枚舉邊
枚舉點
如果所有點落在邊的一側
它是EE
可以看出的是時間復雜度降為O(n^3)
代碼
void checkEdge(Point s[],int n,int p,int q) { bool LEmpty = true , REmpty = true; for(int i=1;i<=n && (LEmpty || REmpty);++i) { if(i!=p && i!=q) ToLeft(S[p],S[q],S[k])? LEmpty=false : REmpty = false; } if(LEmpty || REmpty) S[p].extreme = S[q].extreme = true; } void markEE (Point S[],int n) { for(int i=1;i<=n;++i) S[i].extreme = false; for(int i=1;i<=n;++i) for(int j=1;j<=n;++j) if(i!=j) checkEdge(S,n,i,j); }
算法3 (IC)
前面兩個算法還是很慢的
於是又開始又花了
參考插入排序,出現了這樣一個算法
如果對於一個已經完成的凸包,會有這樣幾種情況
所以對於新出現的點,如果它在裏面,直接忽略,如果在外面,那麽這個點一定是當前的合法EP,同時對原來的進行凸包進行適當調整
判定
但是,我們首要的任務是判定是否在凸包內= =
於是我們引入一個測試 In-Convex-Polygon Text
對於找點,似乎我們可以二分查找!!!!(這裏就直接引視頻中的圖了)
對於凸包上的點排序,每次找到mid點,連接原點與mid,然後ToLeft一次判定點在那邊,然後縮小範圍,最後形成最後一個三角形是,就可以判斷在內還是在外了
那麽時間復雜度似乎到了nlogn??
但是令人失望的是,似乎並不行,類比插排,插排的二分查找缺陷在於動態變換的區間加點不容易 ,如果用vector的話上界也會到n,而凸包面臨的也是如此
所以我們就直接暴力吧,和插排一樣。那麽如何暴力?
對於前面兩種算法,可以再找左邊? ?
所以,我們就直接CCW方向遍歷邊,然後做ToLeft 這個點在所有邊的左側,那麽,顯然在內部,否則在外部。 那麽測試時間為n
總時間復雜度降為O(n^2);
添加
那麽判定完後,要幹的事就是添加了,一個很直觀的想法,類似於黑夜中開燈
虛線部分就是要刪除的實線形成了新的凸包。
可以發現的是,這似乎是兩個臨界的s和t向x連邊拓展,去掉ts,保留st
所以現在的任務就是尋找s和t
這裏的s和t稱之為切線(Tangent)或者支持線(Suppor-Lines)
尋找
那麽繼續觀察,除去s,t 剩下的點v與x的連線,對於v的前驅和後記,必然屬於兩個模式(pattern)即一個在左一個在右
那麽出現以下情況
- 如果狀態是LL 那麽是s
- 如果狀態是RR 那麽是t
- 如果狀態是RL 那麽它位於st上,是保留點
如果狀態是LR 那麽它位於ts上,是刪除點
而對於LR的判定只需要兩次ToLeft
更近一步我們可以發現In-Convex-Polygon Text 似乎也可以那麽處理
如果在內部,則不存在t與s偽代碼
遍歷凸包上的點v 記錄ToLeft(x,v,pre[v]) 與ToLeft(x,v,nxt[v]) 如果狀態是LL 那麽是s 如果狀態是RR 那麽是t 如果狀態是RL 那麽它位於st上 如果狀態是LR 那麽它位於ts上 如果出現過s與t擴展刪除 否則 返回
代碼
void InConvexPolygon Text(Point S[],Point x)
{
bool flag=0;
for(int i=1;i<=n;++i)
if(S[i].extreme)
{
bool P[i] = ToLeft(x,i,S.pre[i]);
bool N[i] = ToLeft(x , i , S.nxt[i]);
if(P[i]==N[i]) flag=1;
}
if(!flag) return ;
else
{
S[x].extreme=1;
for(int i=1;i<=n;++i)
if(S[i].extreme)
{
if(P[i]==0 && N[i]==0) S[i].nxt=x,S[x].pre=i;
else if(P[i]==1 && N[i]==0) S[i].extreme = 0;
else if(P[i]==1 && N[i]==1) S[i].pre=x,S[x].nxt=i;
}
}
}
於是就形成了O(n^2)的算法
算法4(GW)
這個算法時間復雜度為O(n^2) 但是在絕大多數情況下達不到,最優化到達O(n);
對比選擇排序和EE算法
我們可否減少每次查找邊的範圍 ?
答案是顯然的
首先,構成的凸包肯定是環狀的結構。那麽,對於我們當前的還未完成的凸包,一定是從兩個端點出發尋找下一條極邊 直到形成環路
拓展
那麽對於下一個點s有什麽特點? 再次對比前面---ToLeft
所以,ks 一定滿足所有點位於L
那麽算法就很明顯的 對於當前的端點,枚舉所有點做ToLeft更新
然後將k更新為s
那麽第一個點怎麽找,方便起見,引入一個概念
lowest-then-leftmost 點(LTL點) 即最下最左點
以y坐標為第一關鍵字最小,x坐標為第二關鍵字最小
對於第二個極點選排名第二的LTL
偽代碼
找到LTL
i=LTL;
找出第一條的EE
循環
遍歷點
如果新點在選定點的右邊,更新
i更新為k k更新為新點
直到形成環
【學習筆記:CG基礎2】 Convex Hull