算法 判断多个点是否在同一圆周线上_凸包问题——礼物包裹算法
禮物包裹算法最早由Chand&Kapur (1970) [1]提出的,它不僅可以實現二維、三維凸包,還可以實現更高維的凸包,算法復雜度是
,表示輸出的面的數量,表示點集的個數,算法復雜度跟輸出面相關。直觀上,三維的禮物包裹算法,可以看做是在點集的外面包圍了一張紙,每次更新一個新的頂點,用紙覆蓋住它,就像包裹禮物一樣,直至覆蓋整個點集。本篇只考慮三維的凸包求解問題,對于更高維的情況,可以參考Chand&Kapur (1970) [1]以及Preparata&Shamos (1985) [2]的描述。本篇文章主要介紹三維禮物包裹算法來求點集的三維凸包。
文章目錄
- 基本思想
- 退化情況
- 算法實現
- 源碼實現
- 參考
基本思想
禮物包裹算法基于一個簡單的理論:任意一個三維凸包的每一條邊有且只有兩個相鄰面。三維凸包上的每個面用三角形面片表示,若一個凸包有
個面,則凸包共有條邊,根據歐拉公式可知,頂點的個數是。 設三維凸包上的面用三角形面片表示,這里暫不考慮各種退化情況,三維凸包的禮物包裹算法的算法偽碼如下所示:三維空間下點集的凸包實現禮物包裹算法時,必需解決算法中要處理的三個關鍵的子問題:
子問題1
如何設計存儲邊的集合的
數據結構。首先,確定兩點大小關系的對比規則,設兩點
和,若,當且僅當;若,當且僅當;否則,和,若,當且僅當;若,當且僅當;否則,接著,考慮存儲邊的數據結構的設計,每個邊結構表示為
,其中表示邊的兩個端點,記錄與邊相鄰面的個數,通過計數器可以判定與該邊是否存在相鄰面,因為凸包上的每一條邊當且僅當有2個相鄰面。把邊結構存儲在一個平衡二叉樹的數據結構中,邊結構的查詢、插入操作只需要
,其中表示邊的個數。子問題2
給定一個凸包上的三角形面片,如何確定與該面片上任意一條邊相鄰的三角形面片。
圖1. 指定三角形是可以確定一個平面,不同的點與三角的某條邊有不同的夾角設三個點
形成的三角形是凸包上的面片,三個點所在的平面是,從點集中到找一個點,使得由點,和形成的平面與平面的夾角最大,那么由點與邊生成的三角形面片即是凸包的面。以圖1所示為例,點,,,與邊分別可以確定四個平面,這四個平面與平面有一個夾角,選擇夾角最大的平面,即由點與邊確定的平面,因此生成的三角形面片是凸包上的面。圖2. 計算由點pi,p0,p1確定的面,與另一個平面的夾角在介紹了采用的策略后,接下來介紹如何實施策略,即如何判斷哪個點與三角形面片確定的平面
與平面之間的夾角最大。如圖2所示,設平面的法向量是,若三角形點的順序如該圖所示,則法向量為,即,方向為垂直于平面向上。因為在凸包上,所以對于任意一個點(),都在平面上(On the plane)或者平面的上方空間(Above the plane)。圖3. 計算經過點pi和邊p0-p1面的法向量指定一個方向向量
,令同時垂直于法向量和邊,則向量,即有。點在所在的方向上。連接點和點,得到向量,計算在法向量和向量方向上的投影,分別是和,則對于不在平面上的點的夾角可以用等式(1)表示:遍歷點集上的每個點,選擇
值最大的點,就要所求的點,即:由點
向點方向觀察圖2,可以得到橫截面如圖3所示,設是由點,,確定的平面的法向量,向量是向量在如圖3所示的橫截面上的投影,易知向量與法向量垂直。設在方向上的投影為,在方向上的投影為,由相似三角形的性質可得:那么法向量
的方向可以表示為可以注意到,我們只需要找到
值最大的點,并不關心具體的值是多少,因此在計算向量和,可以不對它們進行歸一化處理,即。未歸一化的向量和也不會對等式(4)中法向量的方向造成影響,讀者可以自行證明。確定值最大的點需要遍歷點集,算法的復雜度為。子問題3
如何確定在凸包上的初始三角形面片。
算法的起始階段,先排除點集中重復的點,找到初始的凸包面
,如圖4所示,從點集中找到最小的點,即點的分量最小,若分量最小的點有多個,則取其中分量最小的點,若分量最小的點有多個,則取其中分量最小的點。最小的點是唯一的,經過點作一個平面,平面的法向量表示為,任取一個向量,遍歷點集上的點,使用等式(1)計算得到最大的點,設該點是。采用等式(4),計算出平面的法向量,其中,易知法向量是的形式,第3個分量為0。接著,指定一個與法向量和邊垂直的向量,可以采用等式來計算。遍歷點集上的點,使用等式(2)計算出最大的點,設該點是。至此,初始凸包上的三角形面片的三個點都計算出來了,算法需要三次遍歷點集,算法規模是。圖4. 在凸包上的初始三角形面片p0-p1-p2綜上,在禮物包裹算法中,確定初始的三角形面片,需要時間
;每確定一個新的凸包面,需要時間,其中表示邊,又,即時間復雜度為。因此,三維禮物包裹算法的時間復雜度為。退化情況
多點共面
如果算法實現中,沒有考慮多點共面的情況,會引起錯誤。如圖5所示,求與平面
上邊相鄰在面,得到平面的夾角最大,但是在平面上,存在4個或者大于4個點,圖中有7個點在平面。若隨機地選擇一個點來更新平面F',可能會選擇點,也可能是點,但是顯然點不是凸包的極點,就引發了錯誤。若選擇與邊距離最遠的點來更新平面F',就會選擇點,它一定是凸包上的極點,但可能會引發生成的面的邊可能會發生交叉的情況。如圖9.15所示,考慮邊的相鄰面,最遠點是,連接3點得到凸包上的面;接著考慮邊的相鄰面,最遠點是,連接3點得到凸包上的另外一個三角形面片,這就造成了邊發生相交的情況。圖5. 多個點共面的情況這里介紹一種方法,設三維空間上的點集
在同一個平面上,把點集投影到二維空間上,得到新的點集。三維點投影到二維空間上,可以采用的方法是:設點集所在的平面的法向量為,移除法向量的絕對值最大的分量,即對于任意一個點,投影后的點如等式(9.11)所示。使用二維凸包算法,求出點集
的凸包,設求出的二維凸包為,那么三維點構成的對應的凸多邊形為,其中,點是點在二維空間對應的投影點,。可以把劃分成,,...,共個三角形面片,這種方法生成的三角形面片不會發生相交的情況。以圖6為例,凸包為,就可以劃分為,,,共4個三角形面,如圖7所示。圖6. 選擇與邊距離最遠點更新新面,可能產生的問題圖7. 把凸包上的點,劃分為不相交的三角形面在確定凸包上的初始三角形面片時,也可能有多個點在同一個面上,此時不需將共面點集投影到二維平面上求出子凸包,只需選擇與指定邊距離最遠的點即可,該點一定是凸包的極點,避免了不必要的算法操作。
分母為零的情況
若所求的點
在平面上,則在法向量方向上的投影,顯然,等式(1)的分母為零。此時,需要根據分子的符號判斷該點是否有效。若,說明該點是無效點,直接忽略;若,說明該點是有效的,則夾角最大的點在平面上。以圖8所示為例,點在平面上,在求與三角形面的邊相鄰的凸包面時,點是有效點,但是點都是無效點。圖8. 分母為零的情況算法實現
存儲點的數據結構可以表示為{x,y,z,IsOn},其中,x,y,z分別表示點的X,Y,Z方向上的坐標,IsOn是標志位,是一個布爾數,表示指定點是否是凸包上的極點,這個數據在輸出結果時會起到作用,對于標志位為false的點可以刪除,保留下標志位為true的點。起始時,對于任意一個點
,p.IsOn的值都為false。前面介紹了邊結構可以用{p,q,count}來表示,包括邊的兩個端點p,q和相鄰面個數的記數器count。可以采用點的索引來記錄邊,減少存儲空間,因此邊結構中的p,q表示點的索引,用V表示全局點集,那么邊結構e的端點信息就是V[e.p]和V[e.q]。使用平衡二叉樹來作為存儲邊結構的容器,用T表示,邊結構的查詢、插入操作只需要
的時間,其中,m表示邊的個數。對存儲邊結構的平衡二叉樹T的查詢和插入的表示方法如下所示:- T.find({p,q}),從T中檢索出以V[p],V[q]為端點的邊結構,并將它返回;
- T.insert({p, q}),若T中存在以V[p],V[q]為端點的邊,則將相應的邊結構的count值加1;否則,創建一個邊結構{p,q,1},并把它插入T中。
三維凸包上的面用三角形面片表示,存儲面的數據結構可以表示為{v0, v1, v2},與邊結構類似,為了減少存儲內存,v0, v1, v2表示點的索引,如果給定一個三角形面片F,則它的3個頂點信息就是V[F.v0], V[F.v1], V[F.v2]。三角形面片的朝向是固定的,我們規定,通過等式cross(V[F.v1] - V[F.v0], V[F.v2] - V[F.v0]),計算出的面片的法向量指向凸包外。 算法中還需要設計一種存儲三角形面片的容器,由于我們只需要將三角形面片插入容器首部(尾部)元素、刪除容器首部(尾部)元素、遍歷操作,所以可以采用鏈表這種數據結構作為存儲三角形面片的容器。一方向可以用于存儲候選三角形面片;另一方面用于存儲求出的凸包面片,最終作為結果輸出。對鏈表數據結構List主要的操作的表示方法如下所示:
- List.push(O),把元素O插入鏈表List內;
- List.pop(),從鏈表List中彈出一個元素,并將它返回;
下面給出算法的偽碼,考慮到了輸入的點集存在重合或在同一個平面上,以及上一節描述的多點共面、分母為零的情況。
采用禮物包裹算法求三維空間下點集的凸包giftWrapping(S)
算法第1 ~ 5步,排除點集S中重復的點、以及點集S在同一個平面上的情況。第6 ~ 17步,初始化必要的存儲結構,確定在凸包上的初始三角形面片。接著,進入while循環,從Q中取出一個候選面片F,分別處理面片F的3條邊,從T中查找到相應存儲有相應邊的邊結構E。如果E.count > 1,說明該邊已存在兩個相鄰面,不作任何處理,否則,計算出與其相鄰面的F’,將面F’的另外兩條邊插入到平衡二叉樹T中。
函數getExtremePoint ()實現的功能是:給定一個凸包上的面F,確定與面片F上的邊E相鄰的三角形面片,PS表示一個點的索引集合,因為有可能與E相鄰的三角形面片所在的平面上有多個點,即前面敘述的多點共面的情況,km表示PS中與邊E距離最遠的點的索引。
函數initFirstFacet()實現的功能是:確定在凸包上的初始三角形面片。
函數degenerate ()實現的功能是:解決多點共面這種退化情況,把三維的點
投影到二維空間上,再根據二維凸包算法計算出凸多邊形,最后把結果返回。可以確定,函數degenerate()需要確保凸多邊形CH是單一個方向。三維點投影到二維,以及求二維凸包算法,在前面都已詳細介紹,這里不再贅述算法。圖9. 禮物包裹算法過程最后,以圖9所示為例來演示禮物包裹算法giftWrapping(S)的流程,可以結合偽碼,模擬下算法的過程。圖中的點集為
,分布在一個2x2x2的矩形方塊上,點集的坐標如表1所示。禮物包裹算法會先確定一個初始的三角形面片,設為,把面片存入Q和Z。接著,進入while循環,從Q中取出第1個面片,先處理面片上的邊,產生新的面片;再處理邊,此時出現多點共面的情況,點與邊共面,且產生的夾角最大,隨機啟動“多點共面情況”的子算法,最終產生兩個面片和;最后處理邊,產生新的面片。在處理完第1個三角形面片后,候選面片集。由于Q非空,第2次進行while循環內的迭代操作,處理面片,先處理邊,生成新的面片;再處理邊,生成;最后處理邊,由于該邊的兩鄰面個數為2,已不存在其它相鄰面,不作處理,此時候選面片集。由于Q非空,從中取出一個平面,重復上述操作,直至Q為空,輸出三維凸多邊體的面片,算法結束。如表1所示,由于篇幅限制,只記錄了前4個面片的處理結果。表1. 點集坐標和算法循環體內的處理結果源碼實現
基礎庫源碼鏈接,參見這里,下面是前面所描述的算法的實現。
#define _CRTDBG_MAP_ALLOC參考
[1] Donald R. Chand, and Sham S. Kapur. "An algorithm for convex polytopes."Journal of the ACM (JACM), vol.17, no.1, pp.78-86, 1970.
[2] Franco P. Preparata, and Michael Ian Shamos. Computational geometry. an introduction. Texts and Monographs in Computer Science, New York: Springer, 1
總結
以上是生活随笔為你收集整理的算法 判断多个点是否在同一圆周线上_凸包问题——礼物包裹算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python定时关闭进程_Python子
- 下一篇: opython3l_Python从小白到