數學美 之 判斷線段相交的最簡方法

2021-09-19 09:45:32 字數 3915 閱讀 4483

首發於我的部落格

解析幾何的巔峰

是 向量

那無關過程的狂妄與簡潔

對映著大自然無與倫比的美

如何判斷兩條直線是否相交?

這很容易。平面直線,無非就是兩種關係:相交 或 平行。因此,只需判斷它們是否平行即可。而直線平行,等價於它們的斜率相等,只需分別計算出它們的斜率,即可做出判斷。

但倘若我把「直線」換成「線段」呢——如何判斷兩條線段是否相交?

這就有些難度了。和 直線 不同,線段 是有固定長度的,即使它們所屬的兩條直線相交,這兩條線段也不一定相交。

也許你會說:分情況討論不就行了嘛:

的確,從理論上這是乙個可行的辦法,這也是人們手動計算時普遍採用的方法。

然而,這個方法並不怎麼適用於計算機。原因如下:

那麼,有更好的方法?

當然有。

# 點

class point(object):

def __init__(self, x, y):

self.x, self.y = x, y

# 向量

class vector(object):

def __init__(self, start_point, end_point):

self.start, self.end = start_point, end_point

self.x = end_point.x - start_point.x

self.y = end_point.y - start_point.y

先在此處說明。

對於「判斷兩條直線是否相交」這個問題,我們之所以能迅速而準確地進行判斷,是因為「相交」與「不相交」這兩個狀態有著明顯的不同點,即斜率是否相等

那麼現在,為了判斷兩條線段是否相交,我們也要找出「相交」與「不相交」這兩個狀態的不同點。

假設現在有兩條線段 ab 和 cd,我們畫出它們之間的三種關係:

其中,情況 1 為不相交,情況 2、3 為相交。

作出向量 ac、ad、bc、bd。

首先介紹乙個概念:向量有序對的旋轉方向。這個概念指:對於共起點有序向量二元組(a, b),其旋轉方向為使 a 能夠旋轉乙個小於 180 度的角並與 b 重合的方向,簡記為direct(a, b)。若ab反向共線,則旋轉方向取任意值。

舉個例子:圖一中,direct(ac, ad)為順時針方向。

接下來我們要分析四個值:direct(ac, ad)direct(bc, bd)direct(ca, cb)direct(da, db)

對於圖一,direct(ac, ad)direct(bc, bd)都為順時針,direct(ca, cb)為逆時針,direct(da, db)為順時針。

對於圖二,direct(ac, ad)為順時針,direct(bc, bd)為任意方向,direct(ca, cb)為逆時針,direct(da, db)為順時針。

對於圖三,direct(ac, ad)direct(da, db)為順時針,direct(bc, bd)direct(ca, cb)為逆時針。

不難發現,兩條線段相交的充要條件是:direct(ac, ad) != direct(bc, bd)direct(ca, cb) != direct(da, db)。這便是「相交」與「不相交」這兩個狀態的不同點。

然而你可能會覺得:旋轉方向這麼乙個虛無飄渺的東西,怎麼用程式去描述啊?

再來看一幅圖:

再來定義有向角:

有向角為 向量a逆時針旋轉到與 向量b重合所經過的角度。

不難看出,對於向量ab

這樣一來,我們可以將旋轉方向的問題轉化為求有向角正弦值的問題。而這個問題,是很容易的。

如上圖,記

$$ oa = (x_1, y_1), ob = (x_2, y_2) $$

$$ |oa| = r_1, |ob| = r_2 $$

則$$ sin(\lt oa, ob\gt) $$

$$ = sin \theta $$

$$ = sin (\alpha - \beta) $$

$$ = sin \alpha cos \beta - sin \beta cos \alpha $$

$$ = \frac $$

$$ = \frac $$

而這裡,我們要的只是sin()的符號,而r1r2又都是恆正的,因此只需判斷x1 * y2 - x2 * y1的符號即可。

終於到**部分了,想必大家都已不耐煩了吧。

在向量的輔助下,**顯得異常簡單。

zero = 1e-9

def negative(vector):

"""取反"""

return vector(vector.end_point, vector.start_point)

def vector_product(vectora, vectorb):

'''計算 x_1 * y_2 - x_2 * y_1'''

return vectora.x * vectorb.y - vectorb.x * vectora.y

def is_intersected(a, b, c, d):

'''a, b, c, d 為 point 型別'''

ac = vector(a, c)

ad = vector(a, d)

bc = vector(b, c)

bd = vector(b, d)

ca = negative(ac)

cb = negative(bc)

da = negative(ad)

db = negative(bd)

return (vector_product(ac, ad) * vector_product(bc, bd) <= zero) \

and (vector_product(ca, cb) * vector_product(da, db) <= zero)

一氣呵成,沒有惱人的除法,沒有情況討論,只是純粹的簡單運算。

數學 之 判斷線段相交的最簡方法

引子 如何判斷兩條直線是否相交?這很容易。平面直線,無非就是兩種關係 相交 或 平行。因此,只需判斷它們是否平行即可。而直線平行,等價於它們的斜率相等,只需分別計算出它們的斜率,即可做出判斷。但倘若我把 直線 換成 線段 呢 如何判斷兩條線段是否相交?這就有些難度了。和 直線 不同,線段 是有固定長...

計算幾何之判斷線段相交

計算幾何 判斷線段相交 time limit 1000ms memory limit 65536k description 線段相交 linecross.pas c cpp 問題描述 基本問題 判斷二維平面上的兩條線段是否相交。注意 相交有很多種,這裡指的 相交 是指兩條線段恰好有唯一乙個不是端點的...

計算幾何之判斷線段相交

兩條線段可以看作是兩個向量 一條線段跨立另一條線段的條件是 一條線段的端點分別到另一條線段兩個端點構成的向量在該線段兩側 可能不相交 而兩條線段互相跨立,即相交,就可判斷。模板分享 include include include include define equals a,b fabs a b ...