计算几何个人学习笔记
update:
2026/6/13:
1.修改了不合理的序号
2.按照知乎修改了部分关于皮克定理的不正确内容
3.改正了一些炸掉的markdow
参考文献:
此笔记仅为记录学习以及帮助复习 不包含任何利于理解的东西
零.向量是什么(会的直接忽略)
一、 向量的定义
在二维欧几里得空间 \(\mathbb{R}^2\) 中,平面向量 \(\vec{A}\) 被定义为一个有序实数对:
若已知空间中两点 \(P_1(x_1, y_1)\) 和 \(P_2(x_2, y_2)\),则由 \(P_1\) 指向 \(P_2\) 的有向线段所确定的向量 \(\overrightarrow{P_1P_2}\) 表示为:
二、 向量的代数意义
对于任意向量 \(\vec{A} = (x, y)\):
1. 向量的模长 (Magnitude / Norm)
向量的二范数(几何长度)记作 \(|\vec{A}|\),其形式化定义为:
2. 向量的辐角 (Direction Angle)
向量与正向 \(x\) 轴的夹角 \(\theta\) 的形式化定义为:
三、 向量的代数运算
设已知向量 \(\vec{A} = (x_1, y_1)\),\(\vec{B} = (x_2, y_2)\),以及标量 \(k \in \mathbb{R}\)。
1. 向量加法 (Vector Addition)
2. 向量减法 (Vector Subtraction)
3. 标量乘法 (Scalar Multiplication)
四、 板子
#include <bits/stdc++.h>
using namespace std;
struct Pt{
double x,y;
};
double len,ang;
Pt a,b,c;
Pt add(Pt g1,Pt g2){
Pt r;
r.x=g1.x+g2.x;
r.y=g1.y+g2.y;
return r;
}
Pt sub(Pt g1,Pt g2){
Pt r;
r.x=g2.x-g1.x;
r.y=g2.y-g1.y;
return r;
}
Pt mul(Pt g1,double k){
Pt r;
r.x=g1.x*k;
r.y=g1.y*k;
return r;
}
double get_l(Pt g1){
return sqrt(g1.x*g1.x+g1.y*g1.y);
}
double get_a(Pt g1){
return atan2(g1.y,g1.x);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
a.x=3.0;
a.y=4.0;
b.x=1.0;
b.y=2.0;
c=add(a,b);
len=get_l(a);
return 0;
}
一.向量计算
1. 数乘 (Scalar Multiplication)
- 定义与形式化证明:
已知向量 \(\vec{v} = (x, y)\),实数 \(k\)。数乘运算将向量的每个分量扩大 \(k\) 倍:
证明:由相似三角形性质,若将直角三角形的斜边(向量长度)放大 \(k\) 倍,则其直角边(\(x\) 和 \(y\) 轴的分量)亦必然按比例放大 \(k\) 倍。
- 几何意义:保持向量方向不变(\(k>0\))或完全相反(\(k<0\)),改变向量的长度。
2. 点乘 / 内积 (Dot Product)
- 形式化证明:
已知 \(\vec{A} = (x_1, y_1)\),\(\vec{B} = (x_2, y_2)\)。点乘定义为:
同时满足几何定理:\(\vec{A} \cdot \vec{B} = |\vec{A}||\vec{B}|\cos\theta\)(\(\theta\) 为夹角)。
证明(利用余弦定理):
考虑以原点 \(O\)、点 \(A(x_1, y_1)\)、点 \(B(x_2, y_2)\) 构成的三角形。边 \(AB\) 的长度平方为:
根据三角形余弦定理:
由于 \(|\vec{A}|^2 = x_1^2 + y_1^2\) 且 \(|\vec{B}|^2 = x_2^2 + y_2^2\),两式对照相减立刻得到:
- 几何意义:用于计算夹角及判断正交性。若点乘 \(>0\) 夹角为锐角;\(<0\) 为钝角;\(=0\) 则两向量垂直。
3. 叉乘 / 外积 (Cross Product)
- 形式化证明:
二维向量的叉乘定义为行列式的值:
同时满足几何定理:\(\vec{A} \times \vec{B} = |\vec{A}||\vec{B}|\sin\theta\)。
证明(利用三角函数展开):
设 \(\vec{A}\) 的极角为 \(\alpha\),\(\vec{B}\) 的极角为 \(\beta\),则 \(\theta = \beta - \alpha\)。
代入极坐标成分:\(x_1 = |\vec{A}|\cos\alpha, y_1 = |\vec{A}|\sin\alpha\);\(x_2 = |\vec{B}|\cos\beta, y_2 = |\vec{B}|\sin\beta\)。
根据正弦和差化积公式 \(\sin(\beta - \alpha) = \sin\beta\cos\alpha - \cos\beta\sin\alpha\),上式即:
- 几何意义:其绝对值是以 \(\vec{A}, \vec{B}\) 为邻边的平行四边形的面积。其正负号表达了 \(\vec{B}\) 相对 \(\vec{A}\) 的顺逆时针旋转走向。
二、 直线、线段与点的位置关系
1. 判断点在直线的哪一边
- 证明:
已知直线由有向线段 \(P_1(x_1, y_1) \to P_2(x_2, y_2)\) 唯一确定,判断点 \(Q(x_3, y_3)\) 的方位。
构造向量 \(\vec{A} = \overrightarrow{P_1 P_2} = (x_2 - x_1, y_2 - y_1)\),\(\vec{B} = \overrightarrow{P_1 Q} = (x_3 - x_1, y_3 - y_1)\)。
计算叉积 \(D = \vec{A} \times \vec{B}\)。
证明:由叉积性质 \(\vec{A} \times \vec{B} = |\vec{A}||\vec{B}|\sin\theta\)。
- 若 \(\theta \in (0, \pi)\),则 \(\sin\theta > 0 \implies D > 0\),点 \(Q\) 在 \(P_1 P_2\) 的逆时针方向(左侧)。
- 若 \(\theta \in (-\pi, 0)\),则 \(\sin\theta < 0 \implies D < 0\),点 \(Q\) 在 \(P_1 P_2\) 的顺时针方向(右侧)。
- 若 \(D = 0\),则 \(\sin\theta = 0\),三点共线。
2. 判断两线段是否相交 (跨立实验)
- 形式化证明:
线段 \(AB\) 和 \(CD\) 相交的充要条件是它们彼此跨立对方。即:
- \(C\) 和 \(D\) 在直线 \(AB\) 的两侧:\((\overrightarrow{AB} \times \overrightarrow{AC}) \times (\overrightarrow{AB} \times \overrightarrow{AD}) \le 0\)
- \(A\) 和 \(B\) 在直线 \(CD\) 的两侧:\((\overrightarrow{CD} \times \overrightarrow{CA}) \times (\overrightarrow{CD} \times \overrightarrow{CB}) \le 0\)
- 证明:如果线段相交,直线 \(AB\) 必然将线段 \(CD\) 斩断为两部分。这意味着点 \(C\) 在直线 \(AB\) 的一边(叉积符号为正),而点 \(D\) 必须在直线 \(AB\) 的另一边(叉积符号为负)。两者叉积相乘乘积必然 \(\le 0\)。同理,对直线 \(CD\) 检验 \(AB\) 顶点亦然。
3. 求两条直线的交点
- 证明(利用有向面积比):
已知第一条直线通过 \(A, B\),第二条直线通过 \(C, D\)。设交点为 \(P\)。
证明:过 \(C\) 和 \(D\) 分别向直线 \(AB\) 作垂线,垂线段长度分别为 \(h_1, h_2\)。
由相似三角形可知:\(\frac{|CP|}{|PD|} = \frac{h_1}{h_2}\)。
而三角形 \(ABC\) 的面积 \(S_{ABC} = \frac{1}{2}|AB|h_1\),三角形 \(ABD\) 的面积 \(S_{ABD} = \frac{1}{2}|AB|h_2\)。
因此:\(\frac{|CP|}{|PD|} = \frac{S_{ABC}}{S_{ABD}}\)。利用叉积的有向面积,我们可以精确得到点 \(P\) 分有向线段 \(CD\) 的比值。
代入分点坐标公式,交点 \(P\) 的位置为:
三、 圆与多边形高阶算法
1. 求圆过圆外一点的切线
- 证明:
已知圆心 \(O(x_0, y_0)\),半径 \(R\),圆外一点 \(P(x_1, y_1)\)。求切点 \(T\)。
证明:直角三角形 \(\triangle OTP\) 中,\(\angle OTP = 90^\circ\)。
斜边长度 \(d = |OP| = \sqrt{(x_1 - x_0)^2 + (y_1 - y_0)^2}\)。
切线长 \(L = |PT| = \sqrt{d^2 - R^2}\)。
切角 \(\alpha = \arcsin(R / d)\)。
求出向量 \(\overrightarrow{OP}\) 的极角 \(\beta = \text{atan2}(y_1 - y_0, x_1 - x_0)\)。
则两个切点相对于圆心的极角分别为 \(\beta + \alpha\) 和 \(\beta - \alpha\)。
切点坐标为 \((x_0 + R\cos(\beta \pm \alpha), y_0 + R\sin(\beta \pm \alpha))\)。
2. 判断点是否在多边形内部 (射线法)
- 证明:
从待测试点 \(Q\) 发出一条任意方向的射线(通常水平向右)。
证明:根据若尔当曲线定理 (Jordan Curve Theorem),一条闭合的多边形边界将平面精确切分为内部和外部。从外部无穷远处沿射线移动到点 \(Q\),每穿过一次多边形的边,就发生一次“内部 \(\leftrightarrow\) 外部”的状态切换。
- 穿过奇数次:点 \(Q\) 在多边形内部。
- 穿过偶数次:点 \(Q\) 在多边形外部。
3. 三角形与任意多边形的面积
- 形式化证明(鞋带公式 / 鞋带定理):
对于顶点依次为 \(P_0, P_1, \dots, P_{n-1}\) 的任意多边形,其有向面积 \(S\) 为:
(其中 \(P_n = P_0\))
证明:在平面内任取一点作为原点 \(O\)。将多边形顶点与 \(O\) 连线,划分成 \(n\) 个有向三角形 \(\triangle O P_i P_{i+1}\)。
每个三角形的有向面积为 \(\frac{1}{2} (\overrightarrow{OP_i} \times \overrightarrow{OP_{i+1}})\)。
当多边形边界逆时针包围原点时,多边形外部的三角面积会在求和中正负抵消,最终精确收敛留有多边形的净面积。
4. 求多边形内部整点数量 (皮克定理)
- 证明:
皮克定理 (Pick's Theorem):对于顶点全在格点(整数坐标点)上的简单多边形,其面积 \(S\)、内部整点数 \(I\)、边界上的整点数 \(B\) 满足完美的线性代数关系:
证明概要:可以证明该定理在单位正方形下成立(\(S=1, B=4, I=0 \implies 1 = 0 + 2 - 1\) 正确)。由于任何格点多边形都可以拆分成若干个格点三角形,且此代数式在多边形拼接时具有拓扑加和不变性(共有边上的整点会从 \(B\) 变为 \(I\) 相互抵消),故定理对一切格点多边形普适。
- 如何求 \(B\):利用有理数几何性质,一条从 \((x_1, y_1)\) 到 \((x_2, y_2)\) 的线段,其边界上的整点数(不含起点)等于 \(\gcd(|x_1 - x_2|, |y_1 - y_2|)\)。
四、凸包
1、 凸包的形式化定义
在二维实数欧几里得空间 \(\mathbb{R}^2\) 中,给定一个包含 \(n\) 个点的有限点集 \(S = \{P_1, P_2, \dots, P_n\}\)。
点集 \(S\) 的凸包(记作 \(\text{CH}(S)\))定义为包含 \(S\) 的所有凸组合的最小集合,即:
在几何上,\(\text{CH}(S)\) 是一个凸多边形,其顶点属于 \(S\),且 \(S\) 中的所有点要么在该多边形的边界上,要么在其内部。
2、 核心判定工具:有向面积(叉积)
对于平面上的三个有序点 \(O, A, B\),通过构造向量 \(\overrightarrow{OA} = A - O\) 和 \(\overrightarrow{OB} = B - O\),定义其二维叉积(有向面积的 2 倍)为:
根据叉积的代数性质,转向判定规则如下:
- \(\text{crs}(O, A, B) > 0 \iff\) 从 \(O \to A\) 再到 \(B\) 满足逆时针旋转(左拐)。
- \(\text{crs}(O, A, B) < 0 \iff\) 从 \(O \to A\) 再到 \(B\) 满足顺时针旋转(右拐)。
- \(\text{crs}(O, A, B) = 0 \iff\) \(O, A, B\) 三点共线。
3、 算法一:Andrew 算法(Monotone Chain)
Andrew 算法通过按水平坐标排序,将凸包分为下凸壳(Lower Hull)和上凸壳(Upper Hull)分别构建。
1. 算法流程
- 将点集 \(S\) 按照 \(x\) 坐标升序排序,若 \(x\) 相同则按 \(y\) 坐标升序排序。
- 构建下凸壳:从左到右遍历排序后的点,用单调栈维护顶点。对于新加入的点 \(P_i\),若栈顶两点与 \(P_i\) 的转向关系满足 \(\text{crs}(S_{top-1}, S_{top}, P_i) \le 0\)(非左拐),则将栈顶元素弹出,重复此过程直到满足左拐,再将 \(P_i\) 入栈。
- 构建上凸壳:从右到左倒序遍历点集,重复上述单调栈维护逻辑。
2. 证明
证明(反证法):
假设按照上述规则构建的下凸壳中存在一个局部凹陷。这意味着存在连续三点 \(A, B, C\),使得 \(\text{crs}(A, B, C) \le 0\)。
由于算法在遍历到点 \(C\) 时,会检查栈顶两点(此时即为 \(A, B\))与 \(C\) 的有向面积。若满足 \(\text{crs}(A, B, C) \le 0\),while 循环中的 st.pop_back() 必然会被执行,从而将点 \(B\) 从栈中弹出。
因此,最终留在栈中的任意连续三点必然严格满足 \(\text{crs}(S_{top-1}, S_{top}, P_i) > 0\)(全左拐关系),在数学上确保了多边形的局部与全局凸性。同样地,由于排序后起点和终点具有极值性,它们必然属于拓扑边界,结合上凸壳的逆向扫描,确保了生成的闭合多边形包含所有点。
4、 算法二:Graham Scan 算法
Graham Scan 则是基于极角排序(Polar Angle Sort)的单调栈扫描算法。
1. 算法流程
- 选择点集 \(S\) 中 \(y\) 坐标最小的点作为基准点 \(P_0\)(若 \(y\) 相同则取 \(x\) 最小者)。
- 将其余所有点按照相对于 \(P_0\) 的极角 \(\theta = \text{atan2}(P_i.y - P_0.y, P_i.x - P_0.x)\) 顺时针/逆时针排序。
- 用单调栈正向遍历排序后的点集,同样利用 \(\text{crs}(S_{top-1}, S_{top}, P_i) \le 0\) 的退化判定弹出凹点。
2. 证明
证明概要:
由于基准点 \(P_0\) 处于点集的绝对左下角,其余点相对于 \(P_0\) 的极角严格分布在 \([0, \pi)\) 区间内。
按极角序升序排列后,连接 \(P_0\) 与各顶点的射线 \(\overrightarrow{P_0P_i}\) 随索引 \(i\) 的增加单调向左摆动。
当单调栈向前推进时,若出现 \(\text{crs}(S_{top-1}, S_{top}, P_i) \le 0\),由于 \(P_i\) 位于比 \(S_{top}\) 更靠左的射线上,该几何状态表明 \(S_{top}\) 掉入了由 \(P_0, S_{top-1}, P_i\) 构成的闭合三角形内部,成为了非边界点。将其弹出栈并保持全行程左拐,最终在极角单调转完一圈回到 \(P_0\) 时,栈内保存的序列即构成完备的全局凸壳边界。

浙公网安备 33010602011771号