旋轉卡殼算法

轉自:http://blog.csdn.net/acmaker/article/details/3188177

一、目錄

一些歷史:

1978年, M.I. Shamos's Ph.D. 的論文"Computational Geometry"標誌着計算機科學的這一領域的誕生。 當時他發表成果的是一個尋找凸多邊形直徑的一個非常簡單的算法, 即根據多邊形的一對點距離的最大值來確定。 
後來直徑演化爲由一對對踵點對來確定。 Shamos提出了一個簡單的 O(n) 時間的算法來確定一個凸 n 角形的對踵點對。 因爲他們最多隻有 3n/2 對, 直徑可以在 O(n) 時間內算出。 
如同Toussaint後來提出的, Shamos的算法就像繞着多邊形旋轉一對卡殼。 因此就有了術語“旋轉卡殼”。 1983年, Toussaint發表了一篇論文, 其中用同樣的技術來解決許多問題。 從此, 基於此模型的新算法就確立了, 解決了許多問題。 
他們包括: 
  • 計算距離
    • 凸多邊形直徑
    • 凸多邊形寬
    • 凸多邊形間最大距離
    • 凸多邊形間最小距離
  • 外接矩形
    • 最小面積外接矩形
    • 最小周長外接矩形
  • 三角剖分
    • 洋蔥三角剖分
    • 螺旋三角剖分
    • 四邊形剖分
  • 凸多邊形屬性
    • 合併凸包
    • 找共切線
    • 凸多邊形交
    • 臨界切線
    • 凸多邊形矢量和
  • 最薄截面
    • 最薄橫截帶

二、計算距離

1.凸多邊形直徑

我們將一個多邊形上任意兩點間的距離的最大值定義爲多邊形的直徑。 確定這個直徑的點對數可能多於一對。 事實上, 對於擁有 n 個頂點的多邊形, 就可能有 n 對“直徑點對”存在。 

 

一個多邊形直徑的簡單例子如左圖所示。 直徑點對在圖中顯示爲被平行線穿過的黑點 (紅色的一對平行線). 直徑用淺藍色高亮顯示。


顯然, 確定一個凸多邊形 P 直徑的點對不可能在多邊形 P 內部。 故搜索應該在邊界上進行。 事實上, 由於直徑是由多邊形的平行切線的最遠距離決定的, 所以我們只需要查詢對踵點。 Shamos (1978) 提供了一個 O(n) 時間複雜度計算n點凸包對踵點對的算法。直徑通過遍歷頂點列表, 得到最大距離即可。 如下是1985年發表於 Preparata 和 Shamos 文章中的 Shamos 算法的僞代碼。 
輸入是一個多邊形 P={p1,...,pn}. 
begin
     p0:=pn;
     q:=NEXT[p];
     while (Area(p,NEXT[p],NEXT[q]) > Area(p,NEXT[p],q)) do
          q:=NEXT[q];
          q0:=q;
          while (q != p0) do
               begin
                    p:=NEXT[p];
                    Print(p,q);
                    while (Area(p,NEXT[p],NEXT[q]) > Area(p,NEXT[p],q) do
                         begin
                              q:=NEXT[q];
                              if ((p,q) != (q0,p0)) then Print(p,q)
                              else return
                         end;
                    if (Area(p,NEXT[p],NEXT[q]) = Area(p,NEXT[p],q)) then
                      if ((p,q) != (q0,p0)) then Print(p,NEXT[q])
                      else Print(NEXT[p],q)
               end
end.

此處 Print(p,q) 表示將 (p,q) 作爲一個對踵點對輸出, Area(p,q,r) 表示三角形 pqr 的有向面積。 
雖然直觀上看這個過程與常規旋轉卡殼算法不同, 但他們在本質上是相同的, 並且避免了所有角度的計算。 

如下是一個更直觀的算法:
  1. 計算多邊形 y 方向上的端點。 我們稱之爲 ymin 和 ymax 。
  2. 通過 ymin 和 ymax 構造兩條水平切線。 由於他們已經是一對對踵點, 計算他們之間的距離並維護爲一個當前最大值。
  3. 同時旋轉兩條線直到其中一條與多邊形的一條邊重合。
  4. 一個新的對踵點對此時產生。 計算新的距離, 並和當前最大值比較, 大於當前最大值則更新。
  5. 重複步驟3和步驟4的過程直到再次產生對踵點對 (ymin,ymax) 。
  6. 輸出確定最大直徑的對踵點對。
至此, 上述的過程(僞代碼中的)顯得十分有用, 我們可以從對踵點對中得到其他的信息, 如多邊形的寬度 。

2. 凸多邊形的寬度

凸多邊形的寬度定義爲平行切線間的最小距離。 這個定義從寬度這個詞中已經略有體現。 雖然凸多邊形的切線有不同的方向, 並且每個方向上的寬度(通常)是不同的。 但幸運的是, 不是每個方向上都必須被檢測。 

    我們假設存在一個線段 [a,b], 以及兩條通過 a 和 b 的平行線。 通過繞着這兩個點旋轉這兩條線, 使他們之間的距離遞增或遞減。 特別的, 總存在一個 特定旋轉方向 使得兩條線之間的距離通過旋轉變小。 

    這個簡單的結論可以被應用於寬度的問題中: 不是所有的方向都需要考慮。 假設給定一個多邊形, 同時還有兩條平行切線。 如果他們都未與邊重合, 那麼我們總能通過旋轉來減小他們之間的距離。 因此, 兩條平行切線只有在其中至少一條與邊重合的情況下才可能確定多邊形的寬度。 

    這就意味着“對踵點 點-邊”以及“邊-邊”對需要在計算寬度過程中被考慮。 

 

一個凸多邊形寬度的示意圖。 直徑對如圖由平行切線(紅線)穿過的黑點所示。 直徑如高亮的淡藍色線所示。


    一個與計算直徑問題非常相似的算法可以通過遍歷多邊形對踵點對列表得到, 確定頂點-邊以及邊-邊對來計算寬度。 選擇過程如下:
  1. 計算多邊形 y 方向上的端點。 我們稱之爲 ymin 和 ymax
  2. 通過 ymin 和 ymax 構造兩條水平切線。如果一條(或者兩條)線與邊重合, 那麼一個“對踵點 點-邊”對或者“邊-邊”對已經確立了。 此時, 計算兩線間的距離, 並且存爲當前最小距離。
  3. 同時旋轉兩條線直到其中一條與多邊形的一條邊重合。
  4. 一個新的“對踵點 點-邊”對(或者當兩條線都與邊重合,“邊-邊”對)此時產生。 計算新的距離, 並和當前最小值比較, 小於當前最小值則更新。
  5. 重複步驟3和步驟4(卡殼)的過程直到再次達到最初平行邊的位置。
  6. 將獲得的最小值的對作爲確定寬度的對輸出。

    更爲直觀的算法再次因爲需要引進角度的計算而體現出其不足。 然而, 就如在凸多邊形間最大距離問題中一樣, 有時候更爲簡單、直觀的旋轉卡殼算法必須被引入計算。

 

3.凸多邊形間最大距離

給定兩個凸多邊形 P 和 Q, 目標是需要找到點對 (p,q) (p 屬於 P 且 q 屬於 Q) 使得他們之間的距離最大。 

很直觀地,這些點不可能屬於他們各自多邊形的內部。 這個條件事實上與直徑問題非常相似: 

兩凸多邊形 P 和 Q 間最大距離由多邊形間的對踵點對確定。 
雖然說法一樣, 但是這個定義與給定凸多邊形的對踵點對的不同。 
凸多邊形間的對踵點對本質上的區別在於切線是有向且反向的。 下圖展示了一個例子: 
 
上述結論暗示不單純只是頂點對需要檢測, 而僅僅是特定的頂點對需要被考慮到。 事實上他們只檢測一個基於旋轉卡殼模式的算法確立的平行切線。 
考慮如下的算法, 算法的輸入是兩個分別有 m 和 n 個順時針給定頂點的凸多邊形 P 和 Q

  1. 計算 P 上 y 座標值最小的頂點(稱爲 yminP ) 和 Q 上 y 座標值最大的頂點(稱爲 ymaxQ)。
  2. 爲多邊形在 yminP 和 ymaxQ 處構造兩條切線 LP 和 LQ 使得他們對應的多邊形位於他們的右側。 此時 LP和 LQ 擁有不同的方向, 並且 yminP 和 ymaxQ 成爲了多邊形間的一個對踵點對。
  3. 計算距離(yminP,ymaxQ) 並且將其維護爲當前最大值。
  4. 順時針同時旋轉平行線直到其中一個與其所在的多邊形的邊重合。
  5. 一個新的對踵點對產生了。 計算新距離, 與當前最大值比較, 如果大於當前最大值則更新。 如果兩條線同時與邊發生重合, 此時總共三個對踵點對(先前頂點和新頂點的組合)需要考慮在內。
  6. 重複執行步驟4和步驟5, 直到新的點對爲(yminP,ymaxQ)。
  7. 輸出最大距離。

旋轉卡殼模式確保了所有的對踵點對都被考慮到。 此外, 整個算法擁有線性的時間複雜度, 因爲(除了初始化), 執行步數與頂點數相同。

類似的算法可以被用於凸多邊形間最小距離問題中。

4.凸多邊形間最小距離

給定兩個非連接(比如不相交)的凸多邊形 P 和 Q, 目標是找到擁有最小距離的點對 (p,q) (p 屬於 P 且 q 屬於Q)。 

事實上, 多邊形非連接十分重要, 因爲我們所說的多邊形包含其內部。 如果多邊形相交, 那麼最小距離就變得沒有意義了。 然而, 這個問題的另一個版本, 凸多邊形頂點對間最小距離對於相交和非相交的情況都有解存在。 

回到我們的主問題: 直觀的, 確定最小距離的點不可能包含在多邊形的內部。 與最大距離問題相似, 我們有如下結論: 

兩個凸多邊形 P 和 Q 之間的最小距離由多邊形間的對踵點對確立。 存在凸多邊形間的三種多邊形間的對踵點對, 因此就有三種可能存在的最小距離模式:
  1. “頂點-頂點”的情況
  2. “頂點-邊”的情況
  3. “邊-邊”的情況

換句話說, 確定最小距離的點對不一定必須是頂點。 下面的三個圖例表明了以上結論: 
   
給定結果, 一個基於旋轉卡殼的算法自然而然的產生了: 
考慮如下的算法, 算法的輸入是兩個分別有 m 和 n 個順時針給定頂點的凸多邊形 P 和 Q。
  1. 計算 P 上 y 座標值最小的頂點(稱爲 yminP ) 和 Q 上 y 座標值最大的頂點(稱爲 ymaxQ)。
  2. 爲多邊形在 yminP 和 ymaxQ 處構造兩條切線 LP 和 LQ 使得他們對應的多邊形位於他們的右側。 此時 LP和 LQ 擁有不同的方向, 並且 yminP 和 ymaxQ 成爲了多邊形間的一個對踵點對。
  3. 計算距離(yminP,ymaxQ) 並且將其維護爲當前最小值。
  4. 順時針同時旋轉平行線直到其中一個與其所在的多邊形的邊重合。
  5. 如果只有一條線與邊重合, 那麼只需要計算“頂點-邊”對踵點對和“頂點-頂點”對踵點對距離。 都將他們與當前最小值比較, 如果小於當前最小值則進行替換更新。 如果兩條切線都與邊重合, 那麼情況就更加複雜了。 如果邊“交疊”, 也就是可以構造一條與兩條邊都相交的公垂線(但不是在頂點處相交), 那麼就計算“邊-邊”距離。 否則計算三個新的“頂點-頂點”對踵點對距離。 所有的這些距離都與當前最小值進行比較, 若小於當前最小值則更新替換。
  6. 重複執行步驟4和步驟5, 直到新的點對爲(yminP,ymaxQ)。
  7. 輸出最大距離。
旋轉卡殼模式保證了所有的對踵點對(和所有可能的子情況)都被考慮到。 此外, 整個算法擁有現行的時間複雜度, 因爲(除了初始化), 只有與頂點數同數量級的操作步數需要執行。 

最小距離和最大距離的問題表明了旋轉卡殼模型可以用在不同的條件下(與先前的直徑和寬度問題比較)。 這個模型可以應用於兩個多邊形的問題中。 
“最小盒子”問題(最小面積外接矩形)通過同一多邊形上兩個正交切線集合展示了另一種條件下旋轉卡殼的應用。 

三、外接矩形

1.凸多邊形最小面積外接矩形

給定一個凸多邊形 P , 面積最小的能裝下 P (就外圍而言)的矩形是怎樣的呢? 從技術上說, 給定一個方向, 能計算出 P 的端點並且構由此造出外接矩形。 但是我們需要測試每個情形來獲得每個矩形來計算最小面積嗎? 謝天謝地, 我們不必那麼幹。 

對於多邊形 P 的一個外接矩形存在一條邊與原多邊形的邊共線。 

上述結論有力地限制了矩形的可能範圍。 我們不僅不必去檢測所有可能的方向, 而且只需要檢測與多邊形邊數相等數量的矩形。 

圖示上述結論: 四條切線(紅色), 其中一條與多邊形一條邊重合, 確定了外接矩形(藍色)。


一個簡單的算法是依次將每條邊作爲與矩形重合的邊進行計算。 但是這種構造矩形的方法涉及到計算多邊形每條邊端點, 一個花費 O(n) 時間(因爲有 n 條邊)的計算。 整個算法將有二次時間複雜度。 

一個更高效的算法已經發現。 利用旋轉卡殼, 我們可以在常數時間內實時更新, 而不是重新計算端點。 
實際上, 考慮一個凸多邊形, 擁有兩對和 x 和 y 方向上四個端點相切的切線。 四條線已經確定了一個多邊形的外接矩形。 但是除非多邊形有一條水平的或是垂直的邊, 這個矩形的面積就不能算入最小面積中。 
然而, 可以通過旋轉線直到條件滿足。 這個過程是下屬算法的核心。 假設按照順時針順序輸入一個凸多邊形的n 個頂點。 

  1. 計算全部四個多邊形的端點, 稱之爲 xminP, xmaxP, yminP, ymaxP
  2. 通過四個點構造 P 的四條切線。 他們確定了兩個“卡殼”集合。
  3. 如果一條(或兩條)線與一條邊重合, 那麼計算由四條線決定的矩形的面積, 並且保存爲當前最小值。 否則將當前最小值定義爲無窮大。
  4. 順時針旋轉線直到其中一條和多邊形的一條邊重合。
  5. 計算新矩形的面積, 並且和當前最小值比較。 如果小於當前最小值則更新, 並保存確定最小值的矩形信息。 
  6. 重複步驟4和步驟5, 直到線旋轉過的角度大於90度。
  7. 輸出外接矩形的最小面積。

因爲兩對的“卡殼”確定了一個外接矩形, 這個算法考慮到了所有可能算出最小面積的矩形。 進一步, 除了初始值外, 算法的主循環只需要執行頂點總數多次。 因此算法是線性時間複雜度的。 

一個相似但是鮮爲人知的問題是最小周長外接矩形問題。 有趣的是這兩個問題是完全不同的問題, 因爲存在(儘管極少)最小面積外接矩形和最小周長外接矩形多邊形不重合的多邊形。

2.凸多邊形最小周長外接矩形

這個問題和最小面積外接矩形相似。 我們的目標是找到一個最小盒子(就周長而言)外接多邊形 P 。 

有趣的是通常情況下最小面積的和最小周長的外接矩形是重合的。 有人不禁想問這是不是總成立的。 下面的例子回答了這個問題: 多邊形(灰色的)及其最小面積外接矩形(左邊的)和最小周長外接矩形(右邊的)。 

 


現在, 給定一個方向, 我們可以算出 P 的端點, 以此來確定一個外接矩形。 但是, 就如同面積問題中一樣, 由於有下面的結論, 我們不必檢測每個狀態來獲得擁有最小周長的矩形: 

凸多邊形 P 的最小周長外接矩形存在一條邊和多邊形的一條邊重合。 

這個結論通過枚舉多邊形的一條重合邊有力地限制了矩形的可能範圍。 

 

圖示上述結論: 四條切線(紅色), 其中一條與多邊形邊重合, 確定了外接矩形(藍色)。


因爲與其面積問題相當, 這個問題可以通過一個基於旋轉卡殼的相似的算法來解決。 
下屬算法的輸入是順時針順序給定的一個凸多邊形的 n 個頂點。 

  1. 計算全部四個多邊形的端點, 稱之爲 xminP, xmaxP, yminP, ymaxP
  2. 通過四個點構造 P 的四條切線。 他們確定了兩個“卡殼”集合。
  3. 如果一條(或兩條)線與一條邊重合, 那麼計算由四條線決定的矩形的面積, 並且保存爲當前最小值。 否則將當前最小值定義爲無窮大。
  4. 順時針旋轉線直到其中一條和多邊形的一條邊重合。
  5. 計算新矩形的周長, 並且和當前最小值比較。 如果小於當前最小值則更新, 並保存確定最小值的矩形信息。 
  6. 重複步驟4和步驟5, 直到線旋轉過的角度大於90度。
  7. 輸出外接矩形的最小周長。

因爲兩對的“卡殼”確定了一個外接矩形, 這個算法考慮到了所有可能算出最小周長的矩形。 進一步, 除了初始值外, 算法的主循環只需要執行頂點總數多次。 因此算法是線性時間複雜度的。 

問題處理同樣包含三角形。 有兩個特例, 具體參見洋蔥三角剖分螺旋三角剖分

四、三角剖分

1.洋蔥三角剖分

給定一個平面上的點集, 目標是構造一個點集的三角剖分。 

從Lennes 1911年二次時間複雜度的源算法到Chazelle 1991線性時間複雜度的算法, 前人已經做了許多關於提高三角剖分算法效率的研究。 

這裏的焦點是關於一種特殊的三角剖分, 一種基於對點集進行“剝洋蔥皮”操作。 
考慮平面上一個有 n 個點的集合 S 。 計算 S 的凸包, 並且設 S' 爲在凸包內的點集。 然後計算 S' 的凸包並且反覆執行這個操作直到沒有點剩下。 最後剩下了一個像鳥巢一樣層層覆蓋的凸包序列, 稱爲洋蔥皮集合 S 。 感謝Chazelle的算法, 這個結構能夠在 O(n log n) 時間操作內實現。 

一個點集的洋蔥皮。 注意除了凸多邊形外, 最裏面的結構可能是一條線段或者是一個單一點。 這個圖給出了點的層次信息, 比如點間哪個相對更“深”。


兩個嵌套的凸包間的區域稱爲一個環面。 Toussaint在1986年發表了一個利用旋轉卡殼計算環面三角剖分的簡單算法。 利用這個方法, 一旦構造出洋蔥皮, 就能在現行時間內構造出三角剖分。 進一步, 這個三角剖分有兩個特點: 他的子圖仍然是洋蔥皮, 並且他是一個哈密爾頓圖, 即三角剖分圖的頂點可以是鏈狀的。 

一個環面的三角剖分算法是非常簡單的。 算法輸入一個被凸包 P 包裹的凸包 Q, 他們的頂點都是順時針序的。

  1. 將凸包的邊作爲三角剖分的邊插入。
  2. 計算 P 和 Q 的 x 座標最小的點, 分別稱爲 xmin(P) 和 xmin(Q) 。
  3. 在 xmin(P) 和 xmin(Q) 處構造兩條鉛垂切線, 稱之爲 LP 和 LQ 。
  4. 將 (xmin(P)xmin(Q)) 作爲三角剖分的一條邊插入。
  5. 當前 LP 和 LQ 對應的 p 和 q 點分別是 xmin(P) 和 xmin(Q)
  6. 將線順時針旋轉直到其中一個與一條邊重合。 一個新的頂點由此被一條線“擊”出。
    • 如果他屬於 P (稱爲 p'), 插入 (p'q) 到三角剖分中。 更新當前的點爲 p' 和 p' 。
    • 如果他屬於 Q (稱爲 q'), 插入 (pq') 到三角剖分中。 更新當前的點爲 p 和 q' 。
    • 對於平行邊的情況, 兩條切線都和邊重合, 並且兩個新的頂點被“擊”出(稱他們爲 p' 和 q')。 然後插入 (p'q') , 以及 (pq') 和 (p'q) 到三角剖分中。 更新當前的點爲 p' 和 q' 。
  7. 重複執行上述步驟直到達到開始的最小點。

一個換面的三角剖分如下所示:

 

上述的算法擁有線性時間複雜度。 當對於一個點集進行三角剖分的時候, 一個凸包在整個過程中遍歷(最多)兩次, 最裏面和最外部的凸包都只執行遍歷一次。 因此對於一個 n 個點的三角剖分的總運行時間是 O(n) 。 

另一個有效且與三角剖分有關的問題是基於點集的凸螺旋線的
螺旋三角剖分。 

2.螺旋三角剖分

點集的螺旋三角剖分是基於集合螺旋凸包的三角剖分圖。 
凸螺旋線可以通過如下方法構造:

  1. 從一個特定的端點開始(比如給定方向上的最小點), 這裏取有最小 x 座標的點。
  2. 通過那個點構造一條鉛垂線。
  3. 按照一個給定的方向旋轉線(總保持順時針或者是逆時針方向), 直到線“擊” 出另一個頂點。
  4. 將兩個點用一條線段連接。
  5. 重複步驟3和步驟4, 但是總忽略已經擊出的點。

大體上, 這個過程類似於計算凸包的捲包裹算法, 但是不同在於其循環永遠不會停止。 對於一個凸包上有 h 個點的點集, 存在 2h 個凸螺旋線: 對於每個起點有順時針和逆時針螺旋線兩種。 
 

一個點集(左邊), 及其順時針凸螺旋線, 以最小的 x 座標點作爲初始點。


有趣的是, 一個點集的凸螺旋線和洋蔥皮可以在線性時間內相互轉換。 進一步的, 類似於洋蔥三角剖分, 我們可以定義一個點集的子圖爲凸螺旋線的螺旋三角剖分。 

構造螺旋三角剖分的算法, 雖然是基於環面三角剖分的, 但是卻更爲複雜, 因爲螺旋線必須被分割爲合適的凸包鏈。 假設輸入是一個點集的順時針凸螺旋線 C , 且有 C = { p1 , ... , pn } 。

  1. 將凸螺旋線的邊作爲三角剖分的邊插入。
  2. 從 p1 開始, 尋找點集凸螺旋線上的最後一個點 ph 。
  3. 延長凸螺旋線上的最後一條邊 [p(n-1),pn] 直到其與凸螺旋線相交。 標記交點爲 q' 。
  4. 構造與 C 切於點 q' 的切線。 逆時針旋轉那條線直到他與 C 相交於一點 q 並且平行於 [p(n-1),pn] 。
  5. 將 [p(n-1),q] 插入三角剖分中。
  6. 此操作後將凸螺旋鏈分割稱了兩個部分: 鏈外的部分和鏈內的多邊形區域。 設 Co = { p1 , ... , q } 且 Ci = { ph , ... , q , ... , pn } 。 這個構造過程如下圖所示:
     

    左上角: 構造過程。 右上角: 螺旋外和內部的多邊形區域。 底部: 外部和內部的凸鏈 Co 和Ci 。


  7. 外部螺旋區域可以如環面一樣進行三角剖分。 Co 和 Ci 此時可以被看成一個嵌套凸包。
  8. 內部的多邊形區域可以很容易的在 pn 處星型劃分形成三角剖分。
  9. 這兩個三角剖分的組合構成了整個螺旋三角剖分的結構。

一個螺旋凸包的例子和其三角剖分如下所示:

 

上述的算法是線性時間複雜度的, 算法的時間依賴於環面剖分的運行時間。

3.四邊形剖分

雖然三角剖分是一個更常用的結構, 但最近四邊形剖分在某些特定條件下顯得更適用, 比如 scattered data interpolation 以及 finite element method 等。 

一個四邊形剖分實際上是一個點集的四邊形分割。 一些與三角剖分本質上的區別(除了特別明顯的)應該引起注意: 
首先, 不是所有的點集都存在四邊形剖分。 事實上, 只有偶數點集纔有。 對於奇數點集, 有時需要附加點(稱爲Steiner點)到原集合中, 從而構造一個四邊形剖分。 
同時, 人們經常期望四邊形剖分構造擁有一些“好的”性質, 比如凸的。 這個與三角剖分是不同的。 

有許多簡單的四邊形剖分算法。 比如, 首先考慮點集的三角剖分, 然後加入一個Steiner點到每個三角形內部, 以及每條邊的中間。 連接這些新點構成了四邊形剖分(這是DeBerg提出的)。 

Bose 和 Toussaint 在1997年提出從一個點集的螺旋三角剖分開始, 來構造一個o四邊形剖分。 
如果點集是偶數的, 那麼每隔一個的對角線(在螺旋三角剖分算法中加入的)移除, 構造了一個四邊形剖分。 如果是奇數個點, 那麼從最後一條對角線開始每個隔一條對角線(比如最後一個, 倒數第三個等)進行移除, 在被移除的第一條對角線附近加入一個Steiner點。 下圖展示第一種情況(偶數個點的點集)。 螺旋三角剖分(左邊), 和最終的四邊形剖分(右邊)。 

因爲對角線的移除過程(和必要的更新)花費 O(n) 的時間, 這個四邊形剖分算法與螺旋三角剖分有相同的時間複雜度。 這個算法的優點在於便於理解與實現(一旦凸螺旋線建立), 並且事實上其產生了一個比許多競爭者“更好的”四邊形剖分算法。 

下一個問題集是關於凸多邊形, 特別的, 關於凸包上的操作, 比如合併凸包。

五、凸多邊形屬性

1.合併凸包

考慮如下問題: 給定兩個凸多邊形, 包含他們並的最小凸多邊形是怎樣的? 答案即合併凸包後得到的凸多邊形。 

合併凸包可以通過一個低效的方式實現: 給定兩個多邊形的所有頂點, 計算這些點對應的凸包。 更高效的方法是存在的, 他依賴於多邊形間的 橋 的查找。 下圖描述了這個概念: 

兩個不相交的凸多邊形。 合併後的凸包包含兩個多邊形中的凸包鏈(途中藍色粗實線), 通過多邊形間的橋進行連接(途中藍色虛線)


給定兩個不相交的多邊形, 在多邊形間存在兩條橋。 多邊形相交時, 擁有和頂點數同樣數量的橋, 如下圖所示: 

兩個相交的凸多邊形。 合併凸包只包含多邊形間的橋(圖中虛線所示)。 存在連接八個頂點的八個橋。


合併操作的核心是分治方法。 他同樣用於多邊形中。 一個獲取凸包的十分簡單的方法是將點集分爲兩部分, 分別計算兩個較小點集的凸包, 並且將他們合併。 每個集合再次被分割, 直到元素的個數足夠小(比如說三個或者更少) 因此凸包就能被很容易獲得了。 
Toussaint 提出利用旋轉卡殼來尋找兩個凸多邊形間的橋。 這個方法的主要優點在於其利用回溯, 並且多邊形可以交疊(其他的算法要求多邊形不相交)。 下述結論是他的算法的主要過程: 
給定凸多邊形 P = { p(1) , ... , p(m) } 和 Q = { q(1) , ... , q(n) },一個點對 (p(i), q(j)) 形成 P 和 Q 之間的橋當且僅當:
  1. (p(i), q(j)) 形成一個並踵點對
  2. p(i-1), p(i+1), q(j-1), q(j+1) 都位於由 (p(i), q(j)) 組成的線的同一側。
假設多邊形以標準形式給出並且頂點是以順時針序排列, 算法如下:
  1. 分別計算 P 和 Q 擁有最大 y 座標的頂點。 如果存在不止一個這樣的點, 取 x 座標最大的。
  2. 構造這些點的遂平切線, 以多邊形處於其右側爲正方向(因此他們指向 x 軸正方向)。
  3. 同時順時針旋轉兩條切線直到其中一條與邊相交。 得到一個新的並踵點對 (p(i), q(j)) 。 對於平行邊的情況, 得到三個並踵點對。
  4. 對於所有有效的並踵點對 (p(i), q(j)): 判定 p(i-1), p(i+1), q(j-1), q(j+1) 是否都位於連接點 (p(i), q(j)) 形成的線的同一側。 如果是, 這個並踵點對就形成了一個橋, 並標記他。
  5. 重複執行步驟3和步驟4直到切線回到他們原來的位置。
  6. 所有可能的橋此時都已經確定了。 通過連續連接橋間對應的凸包鏈來構造合併凸包。
上述的結論確定了算法的正確性。 運行時間受步驟1,5,6約束。 他們都爲 O(N) 運行時間(N 是頂點總數)。 因此算法擁有現行的時間複雜度。 

一個凸多邊形間的橋實際上確定了另一個有用的概念:多邊形間公切線。 同時, 橋也是計算凸多邊形交的算法核心。

2.找共切線

公切線是同時與多邊形相切的簡單直線, 並且兩個多邊形都位於線的同一側。 換句話說, 一條公切線是一條與兩個多邊形都相切的線。 一個例子如下圖所示: 

兩個不相交的凸多邊形和一條他們的公切線


事實上, 公切線可以通過多邊形間的一些確定的點對來確立。 因此, 給定兩個不相交的多邊形, 就存在兩個多邊形間兩條公切線, 並且當多邊形相交時, 還有可能存在與頂點數一樣多的公切線。 
用來計算兩多邊形間橋的算法(如歸併算法)同樣可以用來確定公切線。 

另一個“版本”的兩多邊形的公切線是關鍵切線。 那種情況下多邊形分立於線的兩側。 

橋可以用來計算多邊形的交

3.凸多邊形交

給定兩個多邊形, 我們第一個需要討論的問題應該是:“他們相交嗎?”。 Chazelle 和 Dobkin 1980年在他們的一篇叫做“Detection is easier than computation”的論文中發表了一個對數時間級的算法(論文的名字很貼切)。 對於多邊形的交, 許多算法能計算出交集。 有趣的是一個結論(由Guibas提出)證明了多邊形交點和和他們之間的橋是一一對應關係。 
 

兩個多邊形(淺紅色和藍色)和他們的交集(淺紫色)。 交點以紅色標記。 每個交點與一個多邊形之間的橋(標記爲紅色點劃線)有關。


Toussaint在1985年的文獻中利用Guibas的結論, 加上他先前的關於查找橋的算法來計算交點集。 他的算法利用橋來計算交點集。 一旦他們被找到, 與合併凸包的操作類似, 凸鏈以及交點集形成了多邊形的交集。 

算法的細節, 特別是從橋到交點的計算可以在Toussaint的論文中找到: 

G.T. Toussaint. A simple linear algorithm for intersecting convex polygonsThe Visual Computer1: 118-123. 1985. 

下一個問題設計尋找兩個凸多邊形的臨界切線

4.臨界切線

兩個凸多邊形間的臨界切線(一般被叫做CS線)是使得兩個多邊形分居線不同側的切線。 換句話說, 他們分隔了多邊形。
CS線可以應用於motion planning, visibility 和 range fitting。 

下圖是關於兩個多邊形和他們的兩條臨界切線。
 

 

這裏要注意的一點是假設數據是以標準形式給出的, CS線只會在兩個頂點處與兩個多邊形相交。 因此, 一條CS線由多邊形間頂點對確定。 
如下的結論描述了這個點對: 

給定兩個凸多邊形 P, Q, 兩個頂點 p(i), q(j) (分別屬於 P 和 Q) 確定一條CS線當且僅當:

  1. p(i), q(j) 構成多邊形間對踵點對
  2. p(i-1),p(i+1) 位於線 (p(i), q(j)) 一側,同時q(j-1),q(j+1) 位於另一次。
利用這個結論, CS線可以很容易地確定。 只有多邊形間的對踵點對才需要進行測試。 因此, Toussaint建議使用旋轉卡殼。 假設多邊形是以標準形式給出並且是順時針序排列頂點, 考慮如下過程:
  1. 計算 P 上 y 座標值最小的頂點(稱爲 yminP ) 和 Q 上 y 座標值最大的頂點(稱爲 ymaxQ)。
  2. 爲多邊形在 yminP 和 ymaxQ 處構造兩條切線 LP 和 LQ 使得他們對應的多邊形位於他們的右側。 此時 LP 和 LQ擁有不同的方向, 並且 yminP 和 ymaxQ 成爲了多邊形間的一個對踵點對。
  3. 令 p(i)= yminP, q(j)= ymaxQ。 (p(i), q(j)) 構成了多邊形間的一個對踵點對。 檢測是否有 p(i-1),p(i+1) 在線 (p(i),q(j)) 的一側, 並且 q(j-1),q(j+1) 在另一側。 如果成立, (p(i), q(j)) 確定了一條CS線。
  4. 旋轉這兩條線, 直到其中一條和其對應的多邊形的邊重合。
  5. 一個新的對踵點對確定了。 如果兩條線都與邊重合, 總共三對對踵點對(原先的頂點和新的頂點的組合)需要考慮。 對於所有的對踵點對, 執行上面的測試。
  6. 重複執行步驟4和步驟5, 直到新的點對爲(yminP,ymaxQ)。
  7. 輸出CS線。

這個算法基本通過繞着多邊形旋轉切線, 順序查找所有多邊形間的對踵點對。 每次一對對踵點確定後, 執行所有必要的測試。 在上述過程執行完後, 所有的臨界切線都被找到了。 
算法的運行時間由步驟1和步驟6決定, 他們都花費 O(n) 的時間(所有的檢測都花費常數時間。 因爲有 O(n) 的對踵點對, 總的花費爲 O(n))。 

關於凸多邊形的學習, 最後的操作是凸多邊形矢量和

5.凸多邊形矢量和

給定平面上兩個凸多邊形 P 和 Q , P 和 Q 的矢量和, 記爲 P + Q 定義如下:
P + Q = { p + q } 所有的分別屬於 P 和 Q 的 p 和 q 。

多邊形矢量和在 motion planning 中也稱爲 Minkowski 總數。 

考慮上述的定義, 許多問題可以通過詢問集合 P + Q 的組成, 他擁有的性質等等。 下屬結果幫助我們描述多邊形矢量和。
  1. P + Q 是一個凸多邊形。
  2. 頂點集 P + Q 是頂點集 P 和 Q 的和。
  3. 頂點集 P + Q 是 P 和 Q 間的並踵點對集。
  4. 給定分別有 m 和 n 個頂點的 P 和 Q , P + Q 有不多於 m + n 個頂點。

最後, 下屬結論不僅僅描述了這個問題, 同時也提供了一個一個個頂點的增量式計算矢量和的計算方法。 
給定 P + Q 集合的第 k 個向量 z(k), 滿足 z(k) = p(i) + q(j)。 構造在 p(i) 和 q(j) 處構造兩條平行切線, 使得多邊形同時位於各自線的右側。 兩條線分別在 p(i) 和 q(j) 處確定了角 theta(i) 和 phi(j) (如下圖所示)

因此下一個向量 z(k+1) 等於:

  • p(i+1) + q(j) 若 theta(i) < phi(j)
  • p(i) + q(j+1) 若 theta(i) > phi(j)
  • p(i+1) + q(j+1) 若 theta(i) = phi(j)

 

 

 

 

 

 

 

 

下述的多邊形和他們的矢量和作爲一個例子。

 

兩個凸多邊形。 第一個多邊形的邊用紅色標記, 第二個用藍色。



 

 

上述多邊形的矢量和。 其邊的顏色與原多邊形的一致。


用上述的結果, 我們十分容易的就能構造出一個算法來計算矢量和。 第一個向量可以是在給定方向上邊界向量的和(如 y 軸負方向)。 切線構造後, 在計算角度時候更新, 下一個點就很明確了。 我們需要做的只是同時旋轉兩條線到新的位置來確定新的角度。 

算法的正確性來自主要的結論; 他是線性時間複雜度的, 因爲每一步只有一個所要求的向量和集合中的向量被確定, 並且他們只有 m + n 個, 因此總運行時間是 m + n 。

六、最薄截面

1.最薄橫截帶

考慮下述設備放置問題:一個“消費羣體羣”的集合是以個體呈現爲平面上凸多邊形的一個家庭 F 給出的。 我們的目標是找到一個“設備”, 一條平面上的直線, 使得線到消費者的最大距離最小。 

最後一點需要澄清。 直線與任何一個多邊形的距離都是指多邊形上一點到線的正交距離的最小值。 因此,每個多邊形到線的距離是唯一的。 
現在, 給定家庭中各個呈多邊形的成員和平面上的一條直線, 每個多邊形都有一個到線的距離。 因此, 對於整個家庭存在一個最大的線-多邊形距離。 這個距離同時依賴於線與各個家庭成員多邊形。 
這個問題的目標是: 給定一個特定的家庭成員多邊形集, 找到使得這個最大距離最小的線。 這個問題同樣存在着其他版本, 常見的有找一條線使得距離和最小, 或是使得多邊形帶權距離和最小。 

這裏的提出的結論是Robert和Toussaint在1990年發表的。 
主問題等價於找到一個寬最小的帶(一個平面上由兩條平行線爲邊界的區域)和所有的家庭成員多邊形相交。因此, 帶的中心(與帶的邊界線平行等距的線)就是所求的使得最大距離最小的線。 

爲了討論這個問題我們做如下定義: 

平面上的一條直線 l , 其方程爲 ax + by + c = 0 (且 b > 0 或 a = -1)將平面分爲兩個區域:上半平面 Hu(l) 中的點 p = (px,py) 滿足 apx + apy + c >= 0 , 且下半平面 Hl(l) 中的點 p = (px,py) 滿足 apx + apy + c <= 0 。 
通過上面的定義, 如果線是鉛直的, 上半平面爲 x 軸的負方向。 

進一步地, 一個帶可以定義爲一條線的上半平面和另一條(平行)線的下半平面的交集。 

給定一個凸多邊形 P , 一個方向角 theta , 下切線 tl(Ptheta) 是一條與 x 軸正半軸夾角爲 theta 的線, 他與 P相交併且 P 在 tl(Ptheta) 的上半平面。 交點(可能不止一個)稱爲下頂點。 
同樣的, 定義上切線和上頂點。
給定一個家庭的多邊形集合和一個固定的方向角, 就確定了一個下頂點集和上頂點集。 

最後, 考慮下面的結論: 
給定家庭 F 的多邊形集, 和一個方向角 theta , 一個帶 S (由 Hu(l1) 和 Hl(l2) 大於0的交集得到)是 F (在此方向上)最小寬度帶, 當且僅當 F 中存在兩個多邊形 P 和 Q 有
  • P 和 Hu(l1) 的交在 l1 上。
  • Q 和 Hl(l2) 的交在 l2 上。

其主要的結論是: 一個家庭 F 的凸多邊形集的最小寬度帶(一個給定方向 theta 上)由 l1 和 l2 確定當
l1=tl(CH(UP(Ftheta)), theta) 且
l2=tu(CH(LP(Ftheta)), theta) 成立。 

一個家庭的凸多邊形集, 以及給定角度上的最小帶寬。 下頂點和上頂點的凸包, 上述的結論如圖所示。 注意到兩個多邊形和帶的交都只在一個頂點上出現。


因此, 只要確定了家庭多邊形集的下頂點和上頂點的序列, 就能通過計算凸包得到給定方向上最小寬度帶。就如Robert和Toussaint解釋的, 幸運的是這些凸包不需要每次都完全重新計算: 他們需要更新即可。 實際上, 考慮兩個接近的方向:許多(或者是全部)多邊形對於這兩個方向擁有相同的上頂點和下頂點。 這個結果同樣暗示這隻有有限的方向上(當下頂點或上頂點變化時)需要檢測。

這裏的焦點在於旋轉卡殼模型, 而非關係到算法的細節。 本文打算利用旋轉卡殼來計算多邊形的上頂點和下頂點。下面是算法的主要實現過程。 給定一個凸多邊形 P :
  1. 找到擁有最小和最大 y 座標的頂點。 標記爲 p 和 q 並且通過他們構造水平切線。
  2. 逆時針將切線旋轉過 theta 角直到其中一條與其中一個多邊形的邊平行。
  3. 如果頂點在 p 後被擊出(按照逆時針方向), 那麼 p 就是對於角度0(包括)到角度 theta(不包括) 之間的下頂點。 如果頂點是在 q 後被擊出, 那麼 q 就是同樣角度範圍內的上頂點。 這兩個情況當邊平行的時候也可能同時發生。
  4. 更新當前點爲新擊出的頂點, 並更新當前角度。
  5. 重複執行步驟2到步驟4, 同時跟新角度區間, 知道新的角度大等於180度(在哪一點先回到了最初的位置, 但此時次序顛倒)。

線與其中一個多邊形的一條邊平行的方向稱之爲 臨界方向 。 他們只在上頂點和下頂點處發生變化。 對於一個臨界方向, 因爲線穿過兩個頂點, 當逆時針旋轉時下頂點或是上頂點之一被定義爲多邊形與線的一個交點。 

一旦臨界方向(按照順序給出)得到, 一個帶就能在第一個方向上進行計算。 然後, 在第二個臨界方向上, 至少一個上頂點或是下頂點被更新。 因此, 凸包此時需要更新, 而非重新計算一次。 一旦完成上述步驟, 新的帶就構造成功了, 並且他的寬度(邊界間的正交距離)被算出。 對所有臨界方向重複這個操作。 注意到如果任何點處如果產生了一個寬度爲0的帶f, 這個過程就能夠因爲找到一條穿過所有家庭多邊形的線而終止了。 

對於完整的算法描述, 正確性討論和運行時間分析, 見作者的論文:

J.-M. Robert, G.T. Toussaint. Computational geometry and facility location. Proc. Internatioanl Conf. on Operations Research and Management Science, Manila, The Philippines, Dec. 11-15, 1990. pp B-1 to B-19.

源自:http://cgm.cs.mcgill.ca/~orm/rotcal.frame.html


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