二维计算几何基础以及二维凸包
写在前面
注:本文的点到平面距离公式使用了 deepseek AI 生成。
计算几何(Computational Geometry)是计算机科学与几何学交叉形成的前沿学科。
前置知识
二维叉乘
\(\vec{a} \times \vec{b} = x_1y_2-x_2y_1\)。其中 \(\vec a = (x_1,y_1),\vec b =(x_2,y_2)\)。
叉乘的另一种非坐标形式:
其中 \(\theta\) 是向量夹角。
- 叉乘是标量。
共起点向量 \(\vec a\) 和 \(\vec b\),叉乘 \(\vec{a} \times \vec{b}\) 的意义:
- 结果 \(> 0\):\(\vec b\) 在 \(a\) 的逆时针方向(左转)。
- 结果 \(<0\):\(\vec b\) 在 \(a\) 的顺时针方向(右转)。
- 结果 \(=0\):\(\vec a\) 和 \(\vec b\) 贡献(同向/反向,无旋转)。
延伸:判断点 \(C\) 在直线 \(AB\) 的哪一侧。
构造向量 \(\overrightarrow{AB}\) 和 \(\overrightarrow{AC}\),计算叉积 \(\overrightarrow{AB} \times \overrightarrow{AC}\):
- 正:\(C\) 在直线 \(AB\) 的左侧。
- 负:\(C\) 在直线 \(AB\) 的右侧。
- 零:三点共线。
二维点乘
可以参考高中人教版必修 2。
记 \(\vec a =(x_1,y_1)\),\(\vec b =(x_2,y_2)\)。则 \(\vec a \cdot \vec b =x_1 x_2 + y_1 y_2\)。
几何意义可以理解为力做功。
向量模长
可以参考高中人教版必修 2。
设向量 \(\overrightarrow {AB}\) 的坐标为 \((x,y)\),那么它的模长为 \(\sqrt{x^2 + y^2}\)。不难由勾股定理得到。
两点距离公式
记 \(A(x_1,y_1)\),\(B(x_2,y_2)\),那么有 \(|AB| = \sqrt{(x_1 - x_2)^2 + (y_1 - y_2)^2}\),由勾股定理不难得到。
判断线段相交
-
核心思路:快速排斥+判断跨立(叉乘)。
-
规范相交:两条线段有且仅有一个公共点,且该点不是任何一条线段端点,无端点重合。
-
不规范相交:反之。
- 粗筛:用轴对称最小矩形包裹每一条线段,判断矩形是否相交。
- 精判:基于向量叉积的方向特性,判断两条线段是否相互跨立。
此方法规避了浮点数除法精度的丢失和复杂度的不可控。
浮点数判定
绝对不能直接用 \(==\) 判断浮点数是否为 \(0\)。解决方案:定义极小值 \(\epsilon\),竞赛中常取 \(10^{-8}\),通过绝对值判定是否接近 \(0\)。
int sgn(double x){
if(fabs(x)<eps)return 0;
return (x>0)?1:-1;
}
射线法判断点与多边形位置
从点 \(P\) 水平方向向右引一条无限长的射线,统计该射线与多边形所有边的规范相交次数。
- 规范相交次数为奇数:在多边形内部。
- 规范相交次数为偶数:在多边形外部。
直线与圆位置判定
计算圆心到直线的距离 \(d\),判断与圆半径 \(r\) 的大小关系。(初中平面几何)
叉乘求凸包面积
鞋带公式:

简单多边形周长
设点集为 \(P\),\(|P|=n\)。假设已经按顺时针或逆时针排好顺序。
则周长为
三维几何
点乘
给两个三维向量:\(a=(a_x,a_y,a_z)\),\(b=(b_x,b_y,b_z)\)。
叉乘
结果是向量。
用途:求法向量。
性质:
- 反交换律:\(\vec{a} \times \vec{b}=-\vec{b} \times \vec{a}\)
- 不满足结合律
- 数乘性质:\(k\vec{a} \times \vec{b} = \vec{a} \times k \vec{b}\)
- 分配律 \(\vec{a} \times (\vec{b}+\vec{a})=\vec{a} \times \vec{b} + \vec{a} \times \vec{c}\)
- 自身叉乘为零向量:\(\vec{a} \times \vec{a}=\vec{0}\)
点法式方程
平面上一个点 \(P_0(x_0,y_0,z_0)\)
- 平面的法向量 \(\vec{n} =(A,B,C)\)
- 法向量:垂直于平面的向量
- 设点 \(P(x,y,z)\) 为要求的平面上任意一点
弄出一个属于这个平面的向量 \(\overrightarrow{P_0 P}\)。
点到平面的距离公式(deepseek)
已知:平面方程为 \(Ax + By + Cz + D = 0\),平面外一点 \(P_0(x_0, y_0, z_0)\)。求 \(P_0\) 到该平面的距离 \(d\)。
-
在平面上取一点
任取平面上的一个点 \((P_1(x_1, y_1, z_1)\)),它满足平面方程:\[Ax_1 + By_1 + Cz_1 + D = 0 \quad \Rightarrow \quad Ax_1 + By_1 + Cz_1 = -D. \] -
构造向量
考虑从 (P_1) 指向 (P_0) 的向量:\[\overrightarrow{P_1P_0} = (x_0 - x_1,\; y_0 - y_1,\; z_0 - z_1). \] -
法向量
平面的法向量为:\[\mathbf{n} = (A,\; B,\; C). \] -
投影长度即为距离
点 \(P_0\) 到平面的距离等于向量 \(\overrightarrow{P_1P_0}\) 在法向量 \(\mathbf{n}\) 上的投影的绝对值,即:\[d = \left| \frac{\overrightarrow{P_1P_0} \cdot \mathbf{n}}{|\mathbf{n}|} \right|. \] -
计算点积
计算点积:\[\begin{aligned} \overrightarrow{P_1P_0} \cdot \mathbf{n} &= A(x_0 - x_1) + B(y_0 - y_1) + C(z_0 - z_1) \\ &= Ax_0 + By_0 + Cz_0 - (Ax_1 + By_1 + Cz_1). \end{aligned} \]将第一步中的 \(Ax_1 + By_1 + Cz_1 = -D\) 代入:
\[\overrightarrow{P_1P_0} \cdot \mathbf{n} = Ax_0 + By_0 + Cz_0 + D. \] -
代入公式
法向量的模为:\[|\mathbf{n}| = \sqrt{A^2 + B^2 + C^2}. \]因此:
\[d = \frac{|Ax_0 + By_0 + Cz_0 + D|}{\sqrt{A^2 + B^2 + C^2}}. \]该结果与所取点 \(P_1\) 无关,即为点到平面的距离公式。
最终公式:
结构体模板
struct Point{
int x,y;
Point operator+(const Point &a)const{ //加法
return (Point){x+a.x,y+a.y};
}
Point operator-(const Point &a)const{ //减法
return (Point){x-a.x,y-a.y};
}
int operator^(const Point &a)const{ //叉乘
return x*a.y-a.x*y;
}
}
二维凸包
凸多边形定义:
凸多边形是指所有内角大小都在 \([0,\pi]\) 范围内的 简单多边形。
二维凸包定义:
二维凸包是指用最小周长的凸多边形覆盖所有点(包括边界)。
Andrew 算法求凸包
算法流程:
- 将所有点按照 \(x,y\) 坐标进行双关键字排序。
- 用单调栈正向扫描,维护下凸壳。
- 用单调栈逆向扫描,维护上凸壳。
- 单调栈里面的元素即为凸包(如果有重复的 \(1\) 号点,不要计入答案)。
解释:
- 双关键字排序保证了 \(x\) 的单调性,同时排序号的 \(1\) 号点与 \(n\) 号点一定在凸包上。利于判断上下凸壳的边界。
- 单调栈维护的是凸壳的连续“左拐”。类比一个斜率不断递增的过程,但是如果直接计算斜率,分母为 \(0\) 就寄了,所以考虑使用叉乘。如果出现三点共线,中间那个点一定是没有用的,直接连接首尾即可覆盖此类所有点,优化了常数。
下面是一个凸包示意图:

此过程很像斜率优化。
sort(a+1,a+n+1,cmp);
stk[++top]=a[1];
for(int i=2;i<=n;++i){
while(top>=2 && ((stk[top]-stk[top-1])^(a[i]-stk[top]))<=(0.0)){
--top;
}
stk[++top]=a[i];
}
int len=top;
stk[++top]=a[n-1];
for(int i=n-2;i>=1;--i){
while(top-len>=1 && ((stk[top]-stk[top-1])^(a[i]-stk[top]))<=0){
--top;
}
stk[++top]=a[i];
}
int L=top-1;//凸包实际长度
时间复杂度 \(O(n \log n)\),复杂度瓶颈在于排序。
Graham 算法
算法流程:
- 选出纵坐标最小的点。
- 以这个点为极点,水平与竖直方向为极轴建立极坐标系,将其它点按照极角排序。
- 顺序扫描,用单调栈维护凸包上的点,用叉乘弹出非凸包上的点,也就是只保留斜率不断增大的点。
- 最后求出的元素组成的凸多边形就是凸包。

解释:
- 纵坐标最小的点一定在凸包上。它为这个算法选定了一个基准点,因为不在凸包上的点肯定会在循环到某一处时被弹出(单调栈空了),凸包的正确性难以保证。
- 观察上图不难发现,其实要想在凸包上是延长线不断向左旋转得到的结果(多边形外角和等于 \(360\) 度,正好转了一周)。
过程很像 Andrew 算法。但只扫描了一遍,因为 Andrew 的排序并不能在顺序扫描时计算出上凸壳,需要正反各做一遍。
时间复杂度 \(O(n \log n)\),复杂度瓶颈在于排序。
参考资料
- llr 老师的课件。
- 知乎链接1 的图片。
- AI:deepseek。

浙公网安备 33010602011771号