1. 程式人生 > >【學習筆記:CG基礎2】 Convex Hull

【學習筆記:CG基礎2】 Convex Hull

判斷 第一條 尋找 ref 時間復雜度 答案 之前 gin polygon

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