本文选自算法导论某一章节,并用高等数学知识对其进行总结
线段
探讨的问题:
- 对于给定的两个有向线段 p0p1 和 p0p2 ,相对于他们的公共端点 p0 来说,P0p1 是否在 p0p2 的顺时针方向?
- 对于给定的两个线段 p0p1 和 p0p2 ,如果先沿着 p0p1 再沿着 p0p2 前进,那么在点 p1 处是否要向左转?
- 线段 p0p1 和 p0p2 是否相交?
向量积
向量积是计算线段的核心方法。若仅考虑二维平面 (xOy),对于给定的向量 p1=(x1,y1,0) 和 p2=(x2,y2,0) ,则:
p1×p2=⎣⎡ix1x2jy1y2k00⎦⎤=[x1x2y1y2]⋅k=(x1y2−x2y1)⋅k
若将向量看作大小运算,则:
p1×p2=x1y2−x2y1=−p2×p1
- 若 p1×p2 为正,则相对于 原点(0,0) 来说, p1 位于 p2 的顺时针方向;
- 若 p1×p2 为负,则相对于 原点(0,0) 来说, p1 位于 p2 的逆时针方向;
- 若 p1×p2 为零,则表示出现边界情况,此时两个向量是共线的
为了确定相对于公共端点 p0 ,有向线段 p0p1 是在顺时针还是逆时针方向更靠近 p0p1,可以将 p0 作为原点简化问题:用 p1−p0 表示向量 p1′=(x1−x0,y1−y0) 和 p2′=(x2−x0,y2−y0),计算叉积:
(p1−p0)×(p2−p0)=(x1−x0)(y2−y0)−(x2−x0)(y1−y0)
如果叉积为正,那么 p0p1 位于 p0p2 的顺时针方向;反之亦然。
确定连续线段是向左转还是向右转
利用叉积可以避免计算角度问题。方法如下:对于两条连续线段 p0p1 和 p0p2 ,计算叉积 (p2−p0)×(p1−p0) ,若结果为正,则 p0p2 在 p0p1 的顺时针方向,即在 p1 处右转;反之亦然;若叉积为0,意味着 p0,p1,p2 三者共线。
判定两条线段是否相交
需要检查每条线段是否跨越了包含另一条线段是直线。
伪代码如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| #include<stdio.h> #include<stdlib.h>
struct point{ int x; int y; point(){} point(int _x,int _y):x(_x),y(_y){} };
int direction(point p0,point p1,point p2){ return (p1.x-p0.x)*(p2.y-p0.y)-(p2.x-p0.x)*(p1.y-p0.y); }
int min(int a,int b){ return a<b?a:b; } int max(int a,int b){ return a>b?a:b; }
bool on_segment(point p0,point p1,point p2){ if((min(p0.x,p1.x)<=p2.x<=max(p0.x,p1.x)) &&(min(p0.y,p1.y)<=p2.y<=max(p0.y,p1.y))) return true; return false; }
bool segment_intersect(point p1,point p2,point p3,point p4){ int d1=direction(p1,p2,p4); int d2=direction(p1,p2,p3); int d3=direction(p3,p4,p1); int d4=direction(p3,p4,p2); printf("d1:%d\td2:%d\td3:%d\td4:%d\n",d1,d2,d3,d4); if(((d1<0&&d2>0)||(d1>0&&d2<0)) && ((d3<0&&d4>0)||(d3>0&&d4<0))) return true; else if(d1==0&&on_segment(p1,p2,p4))return true; else if(d2==0&&on_segment(p1,p2,p3))return true; else if(d3==0&&on_segment(p3,p4,p1))return true; else if(d4==0&&on_segment(p3,p4,p2))return true; return false; }
int main(){ point p1(1,1),p2(4,4); point p3(3,3),p4(4,0); if(segment_intersect(p1,p2,p3,p4)) printf("线段相交...\n"); return 0; }
|