数学——点到线段的最短距离

发布时间 2023-08-22 19:54:13作者: Aisaka_Taiga

点到线段最短距离的运算与点到直线的最短距离的运算二者之间存在一定的差别,即求点到线段最短距离时需要考虑参考点在沿线段方向的投影点是否在线段上,若在线段上才可采用点到直线距离公式。

通俗的说,我们按照求点到直线的距离作垂线后,交点不一定在线段上。

如图 \(1\) 所示。

image

通常的方法有三种:

解析式法

该算法直接用高中时所学习到的解析几何知识对点到线段的距离进行求解。其基本思想是先判断点在线段端点、点在线上等等的特殊情况,逐步的由特殊到一般,当忽略点在线段上的特殊情况时,判断点到线段方向的垂线是否落在线段上的方法是通过比较横纵坐标的方式来判断,最后把不同的判断情况用不同的几何方式来进行处理计算得出结果。

具体过程是求线段所在直线的解析式,然后再求垂直于当前直线的直线的斜率,带入点求出解析式,求两直线交点,然后判断是不是在直线上,然后进行特判。

面积法

该方法主要是先判断投影点是否在线段上,投影点在线段延长线上时,最短距离长度为点到端点的线段长度;当投影点在线段上时,先使用海伦公式计算三角形面积,再计算出三角形的高,即为最短距离。

以上两种求法过于繁琐,而且在实现的过程中容易出现精度问题。

点名模拟退火

矢量法

由于矢量具有方向性,故一些方向的判断直接根据其正负号就可以得知,使得其中的一些问题得以很简单的解决。

用此方法考虑,我们只需要找到向量 $\overrightarrow{AP} $ 在 $\overrightarrow{AB} $ 方向上的投影,具体如下:

image

上面的 \(\frac{\overrightarrow{AB} }{|\overrightarrow{AB} |}\) 是 $\overrightarrow{AB} $ 方向上的单位向量,其意义是给所求向量确定方向。$(\overrightarrow{AP} \cdot \overrightarrow{AB} ) $ 是两个向量的内积,且 \((\overrightarrow{AP} \cdot \overrightarrow{AB} ) = |\overrightarrow{AP} ||\overrightarrow{AB} |\cos \theta\) ,其中 \(\theta\) 为向量 \(\overrightarrow{AP}\) 与 $\overrightarrow{AB} $ 之间的夹角。\(|\overrightarrow{AB} |\) 是向量长度。

那么 \(\frac{(\overrightarrow{AP}\cdot \overrightarrow{AB} )}{|\overrightarrow{AB} |} = \frac{|\overrightarrow{AP} ||\overrightarrow{AB} |\cos\theta}{|\overrightarrow{AB} |} = |\overrightarrow{AP} |\cos \theta\) 即为上图中线段 \(AC\) 的长度值,不带有方向性。此数值与上述表征方向的 \(\frac{\overrightarrow{AB} }{|\overrightarrow{AB} |}\) 整体构成有大小、有方向的新向量 $\overrightarrow{AC} $,即为 $\overrightarrow{AP} $ 在 $\overrightarrow{AB} $ 方向上的投影向量,\(C\) 为投影点。

根据得到的 \(r = \frac{(\overrightarrow{AP} \cdot \overrightarrow{AB})}{|\overrightarrow{AB} |^{2}}\),由向量的方向性可知:如果情况是上图 \((a)\) 所示,那么 \(0<r<1\);如果是如图 \((b)\) 所示的情况,那么 \(r \ge 1\);如果是如图 \((c)\) 所示的情况,那么得到 \(r \le 0\)

特殊情况如点在线段上、点在端点、点在线段延长线上等等的情况全部适用于此公式,只是作为特殊情况出现,无需另作讨论。这也是矢量算法思想的优势所在。

故根据 \(r\) 值的不同,最短距离:

\[d = \left\{\begin{matrix} |\overrightarrow{AP} | & r\le 0\\ |\overrightarrow{BP} | & r\ge 1\\ |\overrightarrow{AC} | & 0 < r < 1 \end{matrix}\right. \]

c++ 代码:


#include <bits/stdc++.h>

#define int long long
#define DB double
#define N 10010

using namespace std;

int n;
DB ax, ay, ans;
 
struct sb
{
	DB x, y;
	inline DB len(){return sqrt(x * x + y * y);}
} a[N], b[N];
 
inline sb operator + (const sb &a, const sb &b){return (sb){a.x + b.x, a.y + b.y};}
inline sb operator - (const sb &a, const sb &b){return (sb){a.x - b.x, a.y - b.y};}
inline DB dot(sb a, sb b){return a.x * b.x + a.y * b.y;}
inline DB cross(sb a, sb b){return a.x * b.y - a.y * b.x;}
 
inline DB dis(sb p, sb a, sb b)
{
	sb x = p - a, y = p - b, z = b - a;
	if(dot(x, z) < 0) return x.len();
	else if (dot(y, z) > 0) return y.len();
	else return fabs(cross(x, y)) / z.len();
}

参考:https://blog.csdn.net/angelazy/article/details/38489293