飞机上有四个点有两个不同的相对位置:
在位置1中,四个点可以形成凸四边形(即convex hull),而在位置2,它们可以是(并且它们的凸包是三角形)。我的问题是:如何编写算法来确定点是在1位还是2位? (我知道所有四个点的坐标。)
答案 0 :(得分:4)
对于平面中P,Q和R的任意三个点(不共线),您可以通过查看数量的符号来确定角度P-Q-R是逆时针还是顺时针转弯:
(P[0] - R[0]) * (Q[1] - R[1]) - (P[1] - R[1]) * (Q[0] - R[0])
其中P[0]
和P[1]
分别指代P的x坐标和y坐标,对于Q和R也是如此。
现在调用您的四个点P1,P2,P3和P4,并为四个三元组(P1,P2,P3),(P1,P2,P4),(P1,P3,P4)中的每一个计算这些符号, (P2,P3,P4)(这里要小心:上面表达式中点(P,Q,R)的顺序很重要)。如果所有符号相等,或者有两个正号和两个负号,则凸包是四边形。如果有三个正号和一个负号(或相反的方向),则四个点的凸包是一个三角形。或者更简单地说,如果您的符号表示为+1和-1,则将这四个符号相乘。如果产品是+1,则属于四边形;如果为-1,那么你就处于三角形的情况下。
以上假设四个点中没有三个是共线的;我留给你来列举堕落的案例。
由于这是StackOverflow,这里有一些代码(在Python中)。首先是ccw
的定义(使用sign
辅助函数)。
def sign(x):
""" Return the sign of a finite number x. """
if x > 0:
return 1
elif x < 0:
return -1
else:
return 0
def ccw(P, Q, R):
""" Return 1 if P-Q-R is a counterclockwise turn, -1 for clockwise,
and 0 if the points are collinear (or not all distinct). """
disc = (P[0] - R[0]) * (Q[1] - R[1]) - (P[1] - R[1]) * (Q[0] - R[0])
return sign(disc)
然后对四个点进行分类。
def classify_points(P, Q, R, S):
""" Return 1 if the convex hull of P, Q, R and S is a quadrilateral,
-1 if a triangle, and 0 if any three of P, Q, R and S are
collinear (or if not all points are distinct). """
return ccw(P, Q, R) * ccw(P, Q, S) * ccw(P, R, S) * ccw(Q, R, S)
简单测试:应使用结果1
对广场进行分类。
>>> # Test case 1: quadrilateral convex hull
>>> P = 0, 0
>>> Q = 0, 1
>>> R = 1, 0
>>> S = 1, 1
>>> classify_points(P, Q, R, S)
1
结果为-1
的三角形。
>>> # Test case 2: triangle.
>>> P = 0, 0
>>> Q = 0, 3
>>> R = 3, 0
>>> S = 1, 1
>>> classify_points(P, Q, R, S)
-1
这是一个退化的情况(P,Q和S是共线的):
>>> P = 1, 1
>>> Q = 2, 2
>>> R = 5, 7
>>> S = 4, 4
>>> classify_points(P, Q, R, S)
0
请注意,如果您使用不精确的浮点运算,则数值错误可能导致近简并情况被归类为退化,反之亦然。
为了证明上述原因:很容易检查交换ccw
定义中的任何两个输入是否会反转结果的符号,并且交换classify_points
定义中的任何两个输入都会留下符号产品不变。因此,我们可以任意对点进行重新排序,同时影响classify_points
结果。
现在假设P1,P2,P3和P4有一个四边形凸包。然后通过上述观察,我们可以对点进行重新排序,以假设P1,P2,P3和P4以逆时针顺序绕过该四边形的边界。然后,每个ccw
表达式都是1
,因此classify_points
的结果为1
。类似地,如果P1,P2,P3和P4具有三角形凸包,我们可以重新排列,使P1,P2和P3围绕三角形边界逆时针转动,P4在三角形内,并且在这种情况下ccw
符号是1
,1
,-1
和1
。