二维计算几何基础以及二维凸包

写在前面

:本文的点到平面距离公式使用了 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)\)

叉乘的另一种非坐标形式:

\[\vec a \times \vec b = |\vec a| |\vec b| sin \theta \]

其中 \(\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 \cdot \vec b = |\vec a||\vec b|cos \theta \]

\(\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}\),由勾股定理不难得到。

判断线段相交

  • 核心思路:快速排斥+判断跨立(叉乘)。

  • 规范相交:两条线段有且仅有一个公共点,且该点不是任何一条线段端点,无端点重合。

  • 不规范相交:反之。

  1. 粗筛:用轴对称最小矩形包裹每一条线段,判断矩形是否相交。
  2. 精判:基于向量叉积的方向特性,判断两条线段是否相互跨立。

此方法规避了浮点数除法精度的丢失和复杂度的不可控。

浮点数判定

绝对不能直接用 \(==\) 判断浮点数是否为 \(0\)。解决方案:定义极小值 \(\epsilon\),竞赛中常取 \(10^{-8}\),通过绝对值判定是否接近 \(0\)

int sgn(double x){
	if(fabs(x)<eps)return 0;
	return (x>0)?1:-1;
}

射线法判断点与多边形位置

从点 \(P\) 水平方向向右引一条无限长的射线,统计该射线与多边形所有边的规范相交次数。

  • 规范相交次数为奇数:在多边形内部。
  • 规范相交次数为偶数:在多边形外部。

直线与圆位置判定

计算圆心到直线的距离 \(d\),判断与圆半径 \(r\) 的大小关系。(初中平面几何)

叉乘求凸包面积

\[S_{\triangle ABC} = \frac{1}{2}|\overrightarrow{AB}\times \overrightarrow{AC}| \]

\[S_{\text{多边形}} = \frac{1}{2} |\sum_{i=0}^{n-1} (P_i .x \cdot P_{i+1}.y - P_{i}.y \cdot P_{i+1}.x)| \]

鞋带公式:

简单多边形周长

设点集为 \(P\)\(|P|=n\)。假设已经按顺时针或逆时针排好顺序。

则周长为

\[\sum_{i=1}^{n} |\overrightarrow {P_i P_{(i \bmod n) + 1}}| \]

三维几何

点乘

给两个三维向量:\(a=(a_x,a_y,a_z)\)\(b=(b_x,b_y,b_z)\)

\[a \cdot b = a_x * b_x + a_y*b_y + a_z * b_z \]

叉乘

\[\vec{a} \times \vec{b} = (a_yb_z-a_zb_y,a_zb_x-a_xb_z,a_xb_y-a_yb_x) \]

结果是向量。

用途:求法向量。

性质

  • 反交换律:\(\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\)

  1. 在平面上取一点
    任取平面上的一个点 \((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. \]

  2. 构造向量
    考虑从 (P_1) 指向 (P_0) 的向量:

    \[\overrightarrow{P_1P_0} = (x_0 - x_1,\; y_0 - y_1,\; z_0 - z_1). \]

  3. 法向量
    平面的法向量为:

    \[\mathbf{n} = (A,\; B,\; C). \]

  4. 投影长度即为距离
    \(P_0\) 到平面的距离等于向量 \(\overrightarrow{P_1P_0}\) 在法向量 \(\mathbf{n}\) 上的投影的绝对值,即:

    \[d = \left| \frac{\overrightarrow{P_1P_0} \cdot \mathbf{n}}{|\mathbf{n}|} \right|. \]

  5. 计算点积
    计算点积:

    \[\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. \]

  6. 代入公式
    法向量的模为:

    \[|\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\) 无关,即为点到平面的距离公式。


最终公式

\[\boxed{d = \frac{|Ax_0 + By_0 + Cz_0 + D|}{\sqrt{A^2 + B^2 + C^2}}} \]

结构体模板

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。
posted @ 2026-04-01 15:23  lbh666  阅读(7)  评论(0)    收藏  举报