计算几何

计算几何

向量

一般来说,解析几何用函数式表示图形。但在计算几何中,我们用向量表示。

一个具有长度、方向的线可以表示为以原点为起点,以点 \((x,y)\) 为终点的向量。数学表示为:\((x,y)=\mathbf{a}\)\((x,y)=\vec a\)

对于点 \(A,B\),可以用 \(\vec{AB}\) 表示以点 \(A\) 为起点,以 \((x,y)\) 为终点的向量。

向量运算

向量加减法

\[(x_1,y_1)+(x_2,y_2)=(x_1+x_2,y_1+y_2) \]

\[-(x_1,y_1)=(-x_1,-y_1) \]

示例

向量乘法

\[(x_1,y_1)\oplus (x_2,y_2)=(x_1x_2,y_1y_2) \]

它具有乘法的运算律,但一般没有实际几何意义。

数乘

\[k\cdot (x_0,y_0)=(kx_0,ky_0) \]

满足乘法运算律

示例

内积

\[\vec{(x_1,y_1)}\cdot \vec{(x_2,y_2)}=x_1x_2+y_1y_2 \]

向量不能连续内积,只满足交换律和分配律

他可以定义向量长度

向量长度

\[|\vec u|^2=\vec u\cdot \vec u\\ |\vec u|=\sqrt{\vec u\cdot \vec u}=\sqrt{x_1^2+y_1^2} \]


内积的定义也可以表示为:(余弦定理形式)

\[\vec u\cdot \vec v=|\vec u|\cdot |\vec v|\cdot \cos \theta \]

其中,\(\theta\) 为向量夹角(下同)

推导
  • 第一步:极化恒等式(差形式)

我们先证明一个 极化恒等式

\[\vec u \cdot \vec v = \frac{1}{2} \left( |\vec u|^2 + |\vec v|^2 - |\vec u - \vec v|^2 \right) \]

证明

\[\begin{aligned} |\vec u - \vec v|^2 &= (\vec u - \vec v) \cdot (\vec u - \vec v) \\ &= \vec u \cdot \vec u - \vec u \cdot \vec v - \vec v \cdot \vec u + \vec v \cdot \vec v \\ &= |\vec u|^2 + |\vec v|^2 - 2 \vec u \cdot \vec v \end{aligned} \]

移项得:

\[2 \vec u \cdot \vec v = |\vec u|^2 + |\vec v|^2 - |\vec u - \vec v|^2 \]

所以:

\[\vec u \cdot \vec v = \frac{1}{2} \left( |\vec u|^2 + |\vec v|^2 - |\vec u - \vec v|^2 \right) \]

  • 第二步:几何转换——构造三角形

考虑由向量 \(\vec u\)\(\vec v\)\(\vec u - \vec v\) 构成的三角形:

  • \(|\vec u|\):向量 \(\vec u\) 的长度
  • \(|\vec v|\):向量 \(\vec v\) 的长度
  • \(|\vec u - \vec v|\):从 \(\vec v\) 的终点到 \(\vec u\) 的终点的连线长度

\(\vec u\)\(\vec v\) 的夹角为 \(\theta\),而 \(\vec u - \vec v\) 正是这个夹角所对的第三边。

  • 第三步:余弦定理

在三角形中,由余弦定理:

\[|\vec u - \vec v|^2 = |\vec u|^2 + |\vec v|^2 - 2|\vec u||\vec v|\cos\theta \]

移项得:

\[2|\vec u||\vec v|\cos\theta = |\vec u|^2 + |\vec v|^2 - |\vec u - \vec v|^2 \]

即:

\[|\vec u||\vec v|\cos\theta = \frac{1}{2} \left( |\vec u|^2 + |\vec v|^2 - |\vec u - \vec v|^2 \right) \]

  • 第四步:对比结论

将第一步的极化恒等式与第三步的余弦定理结果对比:

\[\vec u \cdot \vec v = \frac{1}{2} \left( |\vec u|^2 + |\vec v|^2 - |\vec u - \vec v|^2 \right) \]

\[|\vec u||\vec v|\cos\theta = \frac{1}{2} \left( |\vec u|^2 + |\vec v|^2 - |\vec u - \vec v|^2 \right) \]

右边完全相同,因此:

\[\boxed{\vec u \cdot \vec v = |\vec u| \, |\vec v| \, \cos \theta} \]

根据这个形式,我们可以推出

\[\cos \theta =\frac{\vec u\cdot\vec v}{|\vec u|\cdot|\vec v|}=\frac{x_1x_2+y_1y_2}{\sqrt{x_1^2+y_1^2}\sqrt{x_2^2+y_2^2}} \]

由此,又能得到:

\[\begin{matrix} \vec u\cdot\vec v>0 & x_1x_2+y_1y_2>0\\ \vec u\cdot\vec v=0 & x_1x_2+y_1y_2=0\\ \vec u\cdot\vec v<0 & x_1x_2+y_1y_2<0 \end{matrix} \]

空间向量

我们也可以把向量扩展到高维,即定义 \(\vec u=(x,\;y,\;z)\)。此时,加减、内积、数乘结论仍能使用。

在三维空间里,我们可以额外定义一种向量运算:

外积

一般来说,两个向量可以表示一个平面,而外积就是直角坐标系内与这个平面垂直的向量。

\[\vec\omega=\vec u\times\vec v=\begin{vmatrix} \vec{e_x} & \vec{e_y} & \vec{e_z}\\ x_1 & y_1 & z_1\\ x_2 & y_2 & z_2 \end{vmatrix}(其中,\vec{e_x}, \vec{e_y}, \vec{e_z})为x,y,z轴方向的向量\\= (y_1z_2 - z_1y_2)\vec{e_x} + (z_1x_2 - x_1z_2)\vec{e_y} + (x_1y_2 - y_1x_2)\vec{e_z}\\ = (y_1z_2 - z_1y_2,\; z_1x_2 - x_1z_2,\; x_1y_2 - y_1x_2) \]

同时,\(\vec\omega\cdot\vec u=0,\ \vec\omega\cdot\vec v=0,\ |\vec\omega|=|\vec u|\cdot|\vec v|\)

在研究平面的计算几何中,我们可以把向量延伸出一维来使用外积运算。计算的结果为:

\[(x_1,\;y_1,\;0)\times(x_2,\;y_2,\;0)=(0,\;0,\;x_1y_2-x_2y_1) \]

不过这样的话,它也不能连续计算了

一般我们认为,在计算几何中, \(|\vec u\times \vec v|=x_1y_2-x_2y_1\)。它的结果也是两个向量张成的平行四边形的面积

我们有:

\[\frac{|\vec u\times\vec v|}{|\vec u|\cdot|\vec v|}=\sin \theta \]

所以我们可以用 \(\cos,\sin\) 值得到 \(\theta\)

表示直线

用直线上一点 \(p\) 与直线方向向量 \(\vec u\) 表示直线 \(l\) 的点集为 \(\{\vec p+k\vec u\},k\in\mathbb{R}\).\

我们用圆心 \(\vec c\) 和半径 \(r\) 来表示一个圆。

位置关系

  • \(a\) 在直线 \(l\) 上:

\[\vec u\times(\vec a-\vec p)=0 \]

  • \(a\) 在直线 \(l\) 左侧:

\[\vec u\times(\vec a-\vec p)>0 \]

  • \(a\) 在直线 \(l\) 右侧:

\[\vec u\times(\vec a-\vec p)<0 \]

  • \(l_1//l_2\)

\[\vec {u_1}\times\vec {u_2}=0 \]

  • \(l_1\perp l_2\)

\[\vec {u_1}\cdot\vec{u_2}=0 \]

  • \(l_1\)\(l_2\) 交点:

\[\vec p + \frac{\vec u_2 \times (\vec p_2 - \vec p)}{\vec u_2 \times \vec u_1} \cdot \vec u_1 \]

  • \(a\)\(l\) 距离:

\[\frac{|(\vec a-\vec p)\times \vec u|}{|\vec u|} \]

  • \(a\)\(l\) 最近点坐标:

\[\vec p + \frac{(\vec a-\vec p)\cdot \vec u}{|\vec u|^2} \cdot \vec u \]

  • \(a\) 在圆 \(o\) 上:

\[|\vec a-\vec c|=r \]

  • \(a\) 在圆 \(o\) 内:

\[|\vec a-\vec c|<r \]

  • \(a\) 在圆 \(o\) 外:

\[|\vec a-\vec c| > r \]

  • 直线 \(l\) 与圆 \(o\) 相交:

\[\frac{|(\vec c-\vec p)\times \vec u|}{|\vec u|} < r \]

  • 直线 \(l\) 与圆 \(o\) 相切:

\[\frac{|(\vec c-\vec p)\times \vec u|}{|\vec u|} = r \]

  • 直线 \(l\) 与圆 \(o\) 相离:

\[\frac{|(\vec c-\vec p)\times \vec u|}{|\vec u|} > r \]

  • 直线 \(l\) 与圆 \(o\) 交点:

\[\text{设 } \vec h = \vec c - \vec p,\ d = \frac{\vec h \cdot \vec u}{|\vec u|},\ e = \sqrt{r^2 - \left(\frac{|\vec h \times \vec u|}{|\vec u|}\right)^2}\\ \text{交点 } = \vec p + \frac{d}{|\vec u|}\vec u \pm \frac{e}{|\vec u|}\vec u_{\perp} \\\text{其中 $\vec u_{\perp} = (-u_y, u_x)$ 为 $\vec u$ 的垂直向量} \]

  • \(o_1\) 与圆 \(o_2\) 相交:

\[|r_1-r_2| < |\vec c_1-\vec c_2| < r_1+r_2 \]

  • \(o_1\) 与圆 \(o_2\) 相切:

\[|\vec c_1-\vec c_2| = |r_1-r_2| \quad \text{(内切)}\\ |\vec c_1-\vec c_2| = r_1+r_2 \quad \text{(外切)} \]

  • \(o_1\) 与圆 \(o_2\) 相离:

\[|\vec c_1-\vec c_2| > r_1+r_2 \]

  • \(o_1\) 与圆 \(o_2\) 包含:

\[|\vec c_1-\vec c_2| < |r_1-r_2| \]

  • \(o_1\) 与圆 \(o_2\) 交点:

\[\text{设 } d = |\vec c_1-\vec c_2|,\ a = \frac{r_1^2 - r_2^2 + d^2}{2d}\\ \vec h = \vec c_1 + \frac{a}{d}(\vec c_2-\vec c_1)\\ \text{交点 } = \vec h \pm \frac{\sqrt{r_1^2 - a^2}}{d} \cdot \text{rot}_{90^\circ}(\vec c_2-\vec c_1) \]

  • \(\Delta ABC\) 面积:

\[\frac 12|\vec{AB}\times\vec{AC}| \]

  • \(a\) 在线段 \(AB\) 上:

\[(\vec a-\vec A)\times(\vec a-\vec B)=0 \quad \text{且} \quad (\vec a-\vec A)\cdot(\vec a-\vec B) \le 0 \]

  • \(AB\)\(CD\) 相交:

\[(\vec{AB}\times(\vec C-\vec A)) \cdot (\vec{AB}\times(\vec D-\vec A)) < 0\\ (\vec{CD}\times(\vec A-\vec C)) \cdot (\vec{CD}\times(\vec B-\vec C)) < 0\\ \text{需同时满足} \]

  • \(AB\)\(CD\) 交点:

\[(\vec{AB}\times(\vec C-\vec A)) \cdot (\vec{AB}\times(\vec D-\vec A)) < 0\\ (\vec{CD}\times(\vec A-\vec C)) \cdot (\vec{CD}\times(\vec B-\vec C)) < 0 \]

向量旋转

我们可以用复数域来更好地解决向量旋转。

。。。

所以,\(\vec {(x,y)}\) 逆时针旋转 \(\theta\) 后的 \(\vec {(x',y')}\)

\[\vec{(x\cdot\cos \theta - y\cdot \sin\theta,x\cdot \sin\theta+y\cdot\cos\theta)} \]

精度问题/代码问题

在计算几何内,我们不可避免地要遇到精度问题。这在计算机界十分知(致)名(命)。你当然可以用python,但如果不用呢?

首先,我们至少要用 long double,一般会用 __float128。但这也不好解决。

  • 小数
    • 如果我们要处理 \(\vec{(10^{18},10^{18})}\)\(\vec{(10^{18},10^{18}+1)}\) 的向量运算,那么需要精度可能会到 \(10^{-18}\) 级别。
  • \(\sin,\cos...\)
    • c++实现这些操作依赖泰勒展开。虽然蒟蒻不会,但我们可以了解到泰勒展开有无限项,必然只会保留部分项。
  • ...

再看上面,为什么我们要用外积表示左右?因为几种积运算都不容易出现精度大损失,没有除法问题不大。不过,精度误差会逐步累积,所以我们要尽量简化计算,或者是在整数范围内计算解决掉精问题。

这里我们也可以解释计算机为什么不涉及解析几何了——你总不能让计算机解方程还不用分数吧?

代码实现上,计算几何也极度难写。分类讨论极多,在精度问题下,以至于连判断相等都要小心。

平时刷题时,如果真遇见了计算几何,那就可以用deepseek提前写的封装类实现各种。场上也尽量用封装,便于调试。不过遇见的不多就是了。

凸包

凸包的定义很简单,就是平面上若干点,求一个最小的多边形包含所有点。想想橡皮筋套住钉子,那就是凸包。

对于每条边,多边形必然只在它一侧。利用这一点,我们可以实现查找凸包。

Andrew算法

我们知道,\(x\) 坐标最小和最大的点一定在凸包上。我们按上下侧,以这两个点为分界分为上凸壳和下凸壳。

我们使用单调栈,用 \(x\) 坐标排序,顺序枚举下凸壳,逆序枚举上凸壳。过程中,如果当前点在之前凸壳末尾的右侧,就把凸壳结尾删去。

实现时,判断左右侧用前面的向量知识维护,排序为防止同样 \(x\) 坐标,不要忘记按 \(y\) 坐标为第二权值。

Graham扫描法

和上一个算法基本一样,但按极角排序。容易发现思路本质相同。

比较

Andrew算法需要两遍,而Graham虽然只要一遍,但极角容易爆精,需要特殊方法排序。

旋转卡壳(旋转卡尺)

解决有关凸包信息的问题。

我们可以枚举凸包上每一条边,找到一条平行直线上一点,满足直线不经过凸包内部。这样我们可以维护凸包上关于对顶点对(就是刚刚枚举的,满足经过两点平行线不经过凸包)的信息。

  • 直径:转的时候,维护距离。直径必定是对顶点对最大距离
  • 最小矩形覆盖:在两个垂直方向上旋转卡壳,维护面积。

平面最近/远点对

距离:

  • 欧式距离:\(\sqrt{(x_1-x_2)^2+(y_1-y_2)^2}\)
  • 曼哈顿距离:\(|x_1-x_2|+|y_1-y_2|\)
  • 切比雪夫距离:\(\max\left(|x_1-x_2|,|y_1-y_2|\right)\)

其中,切比雪夫距离和曼哈顿距离可以通过绕原点旋转 \(45\degree\) 来转换。

最近点对: 可以采用 人类智慧 分治法。

最远点对: 凸包直径。

半平面交

平面上,有很多半平面(直线一侧的面),求解它们的交。

多边形面积

凸多边形,我们可以把它剖成不同的三角形。而凹多边形,我们可以利用以下性质:

向量的叉积会根据不同方向而发生符号变化。利用此性质可以求凹多边形面积。

\[S=\left|\frac 12\sum_{i=1}^{n-1}a_i\times a_{i+1}\right| \]

posted @ 2026-08-09 22:02  c_haoc  阅读(6)  评论(0)    收藏  举报