目录[隐藏] |
[编辑]知识准备
- 图论
- 最短路
[编辑]操作
这个模块讨论几个计算某些几何问题的算法,这些算法大多基于下列两个操作:叉积和反正切。
[编辑]叉积
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
给出一组直线所确定的多边形,判断多边形是否是简单的(任意两条不相邻的线段不会相交)和凸的.