C++如何判断一个点是否在凸多边形内(向量积法)
判断点是否在凸多边形内可采用向量积法,通过计算点与每条有向边的叉积符号是否一致实现。需注意顶点顺序(逆时针或顺时针)及边界上叉积为零的处理,否则易导致错误结果。
判断一个点是否在凸多边形内部,在计算机图形学和几何算法中是个非常经典的问题。说到具体实现,向量积法(也叫叉积法)是效率很高且思路清晰的一种解法,但它有几个关键细节,一旦没注意,结果就可能出问题。
向量积法的核心逻辑
先把这个方法的本质说透:给定一个凸多边形和一个测试点,只要这个点在多边形内部(包括边界上),那么它相对于每条有向边计算出来的叉积,符号必须完全一致——要么全部大于等于0,要么全部小于等于0。这取决于多边形的顶点是逆时针排列还是顺时针排列。
换句话说,我们并不是去算距离或者角度,而是通过叉积cross来判断点相对于每条边的“朝向”是否统一。假设顶点已经按逆时针顺序存好,那么对于每条边v[i] → v[(i+1)%n],计算从v[i]指向测试点p的向量与这条边向量的叉积即可。
一个标准的2D叉积函数长这样:
int cross(const Point& a, const Point& b, const Point& c) {
return (b.x - a.x) * (c.y - a.y) - (b.y - a.y) * (c.x - a.x);
}
结果大于0,表示c在a→b的左侧——对应逆时针方向下的“内侧”。
顶点顺序与边界处理
这才是最容易翻车的地方。顶点顺序错了,符号判断的全部方向都会颠倒;边界上的点叉积为0,这时候必须允许等于0的情况,否则就会漏判。
实践中需要注意几点:
- 判断顶点顺序:最简单的方法是用
cross(v[0],v[1],v[2])初步判断,但更稳的做法是算一下有向面积,根据符号确定是逆时针还是顺时针 - 统一判断条件:如果是逆时针,直接用
cross(v[i], v[(i+1)%n], p) >= 0看是否全为正;如果发现某个叉积小于0,立刻返回false - 顺时针的情况:把条件换成
<= 0,或者干脆把顶点顺序翻转成逆时针,省得写分支逻辑 - 边界判断的精度问题:整数坐标用
== 0比较是安全的;浮点坐标必须引入epsilon,比如abs(cross_val) < 1e-9
为什么凹多边形不行
这个局限性必须讲清楚。凹多边形的问题是:它的某些边的“内侧”方向是不一致的。同一个内部点,可能在某条边的左侧,却在另一条边的右侧——叉积符号自然有正有负,但点确实在内部。这时候向量积法直接失效。
这不是代码写错了,而是数学前提被破坏。如果你不确定输入的多边形是否凸,有两个选择:要么先做一遍凸性检测(比如检查所有连续三个顶点的叉积是否同号),要么直接改用射线法或winding number法。
常见的坑:
- 没验证多边形是否凸,直接套用向量积法,结果在凹多边形上时对时错
- 把叉积当成标量距离去比较绝对值大小,完全误解了方向判断的核心
- 循环里忘了取模
(i+1)%n,导致最后一条边v[n-1]→v[0]被遗漏,或者访问越界
整数坐标下的性能与安全细节
用int做叉积的中间计算是最稳的——没有精度丢失,没有NaN风险,编译器优化也最充分。但有一个隐藏问题:坐标范围如果接近±2e9,(b.x - a.x) * (c.y - a.y)这类乘法就可能溢出int。
几点优化建议:
- 保守起见,用
long long存叉积结果,特别是当坐标虽然是int但取值范围很大的时候 - 避免重复计算:把边向量
dx = v[(i+1)%n].x - v[i].x和dy = v[(i+1)%n].y - v[i].y提前算好,然后直接算dx*(p.y - v[i].y) - dy*(p.x - v[i].x) - 短路判断:一旦遇到一个叉积符号不符,立刻
return false,不必等全部算完 - 决定是否包含边界:用
>= 0代表包含边界,> 0代表严格内部
真正容易被忽略的,是顶点顺序的隐式依赖和整数溢出边界——它们不会报错,但会在特定的输入下静默地给出错误结果。
Windows 10 是一款微软推出的经典操作系统,拥有硬件兼容性与多任务处理能力。它更偏向把系统状态查看和常用调节动作放在一起,适合需要持续观察和微调设备状态的场景。
极度公式是一款跨平台专业LaTeX公式识别编辑软件,支持OCR公式识别和多平台编辑。和使用说明,避免使用,享受完整功能与稳定支持。做扫描整理、文字提取和表格转换时,它能把识别后的处理步骤接得更顺,资料录入这类场景会省下不少时间。
















