目錄[隱藏] |
[編輯]知識準備
- 圖論
- 最短路
[編輯]操作
這個模塊討論幾個計算某些幾何問題的算法,這些算法大多基於下列兩個操作:叉積和反正切。
[編輯]叉積
u和v的叉積被表示成u x v。兩個三維的向量u,v的叉積是下列行列式(i,j,k爲x,y,z軸上單位向量):
| i j k | | Ux Uy Uz | | Vx Vy Vz |
即:
(Uy*Vz-Vy*Uz)i + (Uz*Vx-Ux*Vz)j + (Ux*Vy-Uy*Vx)k
z分量爲0時,上面式子就化爲2維的兩向量叉積了。結果只有z分量。
即:
| Ux Uy | | Vx Vy | Ux*Vy-Uy*Vx
(注意!二維的叉積較爲猥瑣。。其實叉積最早就是定義在三維空間內的,當z=0時的特殊情形纔是二維向量積。“二維向量”表達式求出的是一個超出平面的向量。。這個向量的方向我們不關心,但有時又要利用它。。。(例如求多邊形面積)故上式不甚嚴格)
叉積有3個特點:
- 兩個向量的叉積是一個與這兩個向量同時垂直的向量。
- 叉積的大小等於下面3個量的乘積:
- u的大小
- v的大小
- u,v夾角的正弦。
當然與u,v同時垂直的向量有兩個方向,叉積的方向取決於u在v的右邊還是在v的左邊。
[編輯]點積
點積的值由以下三個值確定:
- u的大小
- v的大小
- u,v夾角的餘弦。
在u,v非零的前提下,點積如果爲負,則u,v形成的角大於90度;如果爲零,那麼u,v垂直;如果爲正,那麼u,v形成的角小於90度。
[編輯]反正切
反正切函數對於一個給定的正切值,返回一個在-pi/2到pi/2之間的角(即-90度至+90度)。C中另外有一個函數atan2,給出y分量和x分量(注意順序!),計算向量與x正半軸的夾角,在-pi到pi之間。它的優點就是不需擔心被0除,也不需爲了處理x爲負的情況而寫代碼修改角。atan2函數幾乎比普通的atan函數更簡單,因爲只需調用一次。
[編輯]全面考慮問題
這些幾何問題大多都產生很多特殊情況。注意這些特殊情況並且要保證自己的程序能處理所有的情況。
浮點運算也會帶來新問題。浮點運算很難精確,因爲注意:計算機只能計算有限的精度。特別地,要通過判斷兩個值的差是否在一個很小的範圍內來判斷是否相等。
[編輯]計算幾何算法
這裏是一些能幫助你計算幾何問題的東西。
[編輯]三角形面積
爲了計算由點(A,B,C)構成的三角形的面積,先選取一個頂點(例如A),再向剩餘兩個頂點作向量(令u=b-a, v=c-a)。三角形(A,B,C)的面積即爲u,v叉積長度的一半。
設兩邊長爲a,b,夾角爲A,面積S即 S=1/2*a*b*sinA
另一個求三角形面積的變通方法就是用海倫公式。如果三角形三邊長爲a,b,c,令s=(a+b+c)/2,那麼三角形面積就是:
sqrt(s*(s-a)*(s-b)*(s-c))
[編輯]兩條線段平行嗎?
爲了判斷兩條線段是否平行,分別沿兩條線段建立向量,判斷叉積是否(幾乎爲)零。
[編輯]多邊形面積
由點(x1,y1)...(xn,yn)組成的多邊形的面積等於下列行列式的值:
1 | x1 x2 ... xn | --- | | 2 | y1 y2 ... yn |
也等於下面的式子的值:
1/2*abs(x1y2 + x2y3 + ... + xny1 - y1x2 - y2x3 - ... - ynx1)
就是選取原點作標準 連接原點和個頂點 並且兩兩個地計算叉積並加起來
[編輯]點到直線的距離
點P到直線AB的距離也可以由叉積給出,準確的說,d(P,AB) = |(P - A) x (B - A)| / | B - A| 。
點P到由點A,B和C確定的平面的距離,令n = (B - A) x (C - A),那麼d(P,ABC) = (P-A) · n / |n|。
[編輯]點在直線上
如果點到直線的距離是0,那麼點在直線上。
[編輯]點都在直線的同側
只講兩個點的情況。如果要確定點C和D是否在直線AB同側,計算(B - A) x (C - A)和(B - A) x (D - A)的z分量。如果同號(或如果積爲正),那麼點C,D在直線AB同側。
[編輯]點在線段上
爲了求出點C是否在線段AB上,先判斷點C是否在直線AB上,再判斷線段AB的長度是否等於線段AC長度與線段BC長度之和。
[編輯]點在三角形內
要確定點A是否在三角形內,首先選擇一個三角形內部的點B(重心就很不錯)。接下來,判斷點A,B是否都在三邊所在的三條直線的同側。
[編輯]點在凸多邊形內
方法同上
[編輯]四點(或更多)共面
如果要確定一組點是否共面,任選3個點。如果對於任意點D,有((B - A) x (C - A)) · (D - A) = ~0,那麼這些點共面。
[編輯]兩條直線相交
平面內兩條直線相交當且僅當直線不平行。
空間內,兩直線AB,CD相交則AB,CD不平行,且點A,B,C,D共面。
[編輯]兩條線段相交
平面內,兩條線段相交當且僅當A,B在直線CD異側且C,D在直線AB異側。
注意兩個判斷都是必須的,例如第三種情況第一個判斷爲true,但第二個判斷說明線段AB,CD不相交。在空間中,計算下面方程組,其中i,j未知:
Ax + (Bx - Ax) i = Cx + (Dx - Cx) j
Ay + (By - Ay) i = Cy + (Dy - Cy) j
Az + (Bz - Az) i = Cz + (Dz - Cz) j
如果方程組有解(i,j)滿足0<=i<=1,0<=j<=1,那麼兩線段相交於點(Ax + (Bx - Ax)i, Ay + (By - Ay)i, Az + (Bz - Az) i)
[編輯]兩直線的交點
在平面內的兩條直線AB,CD,求交點最直接的方法就是解下列的二元一次方程組:
Ax + (Bx - Ax)i = Cx + (Dx - Cx) j
Ay + (By - Ay)i = Cy + (Dy - Cy) j
交點是:
(Ax + (Bx - Ax) i, Ay + (By - Ay) i)
空間內,解同樣的方程組來判斷交點,交點是:
(Ax + (Bx - Ax)i, Ay + (By - Ay)i, Az + (Bz - Az)i)
[編輯]判斷平面內多邊形的凹凸性
要判斷平面內一多邊形是否爲凸,沿着順時針方向掃一遍。對於每三個點(A,B,C),計算叉積(B - A) x (C - A)。如果叉積的z分量均爲負,多邊形則爲凸多邊形。
[編輯]點在凹多邊形內
要確定點是否在凹多邊形內,任選一點作射線,計算相交次數。如果與多邊形相交於一點或一邊,則換一個方向,否則,點在多邊形內當且僅當射線與點相交次數爲奇數。
這個方法也可以擴展到三維(或更高),但此時應相交於面(再判斷),不是在點上或邊上。
[編輯]幾何方法
這些幾何方法介紹了一些技巧,可以用來優化時間和求得更精確的解。
[編輯]蒙特卡洛方法(Monte Carlo)
第一個方法是建立在隨機化之上的。它不去直接計算某件事的概率,而是以一個隨機事件來模擬,求得事件發生的頻率。如果次數足夠,頻率和概率的差別會很小。
這對於確定某些東西來說是很有用的,例如計算某個圖形的面積。它不去直接計算面積,而是建立一個已知大小的區域,每次向該區域扔一個“飛鏢”,並計算擊中區域內圖形的頻率。如果計算足夠精確,就可以得到實際面積的一個足夠好的近似值。
這個方法要得到一個足夠小的相對誤差(誤差於實際值之商),需要大量成功發生的事件。如果發生的概率很小,結果就不好了。
[編輯]分割技術
分割技術可用來優化時間。這需要把平面分割成小塊(通常是分成小格,但有時也會用角度或其它方法),並將元素放入合適的區域中。當檢查圖案的某部分時,只有有該圖案的格子會被檢查到。所以,時間可以大大地優化,比如,要確定到定點的距離小於某個值的元素(此時圖案是個圓),或求是否相交(此時圖案爲直線)。
[編輯]轉化爲圖
有時看起來像幾何問題的問題實際上卻是一個圖的問題。因爲輸入是平面內的點,並不代表問題是幾何問題。
[編輯]例子
[編輯]Point Moving
給定一組線段和點A,B,能否在不穿過線段的情況下從A移動到B?
線段將平面分割成不同部分,檢查A,B是否在一個部分。
[編輯]Bicycle Routing
給出有起點和終點的一組建築物,找出從建築物A到B不穿過任何建築物的最短路線。
題解:圖論問題。以建築物的起點和重終點爲點,兩點之間在不徑直穿過任何建築物時連一條邊,邊權爲兩點之間的距離。這樣就把原問題轉化成了最短路問題。
[編輯]Maximizing Line Intersections
給出平面內一組點,找到能被一條直線能相交到的最多線段。
題解:很顯然,直線必須經過兩個交點。這樣,枚舉每對交點,計算此時直線的交點數。可用分割法優化。
[編輯]Polygon Classification
給出一組直線所確定的多邊形,判斷多邊形是否是簡單的(任意兩條不相鄰的線段不會相交)和凸的.