凸多边形的充要条件是所有连续三顶点叉积同号(全≥0或全≤0);需按序遍历每组(p[i−1],p[i],p[i+1])并跨接首尾,允许零叉积但不可正负混杂,建议用sgn函数处理浮点误差。

用叉积符号判断顶点转向是否一致
凸多边形的充要条件是:所有连续三顶点构成的向量叉积同号(全 ≥ 0 或全 ≤ 0),即顶点按顺序(顺时针或逆时针)遍历时,拐向始终一致。C++ 中无需调用几何库,只需手写二维叉积函数 cross 并遍历每组 (p[i-1], p[i], p[i+1])。
注意:输入顶点必须是**按顺序排列的简单多边形**(无自交、首尾不重合),且至少 4 个点;若顶点顺序混乱或含重复点,结果无意义。
- 叉积公式:
cross(a, b, c) = (b.x - a.x) * (c.y - b.y) - (b.y - a.y) * (c.x - b.x) - 对 i 从 1 到 n−2,检查
cross(p[i-1], p[i], p[i+1]);再补上首尾跨接:i = n−1 时用p[n-2], p[n-1], p[0],i = 0 时用p[n-1], p[0], p[1] - 允许零叉积(三点共线),但不能出现正负混杂;建议用
sign = (val > 0) - (val 统一归为 −1/0/+1,再比对非零值的符号
处理浮点误差与共线点的鲁棒写法
实际坐标常为 double,直接判 == 0 或比较符号易受精度影响。应引入小阈值 EPS = 1e-9,把叉积绝对值小于 EPS 的视为共线(即叉积为 0),仅对 |cross| > EPS 的值记录符号。
常见错误:未跳过共线点就参与符号比较,导致误判;或用 if (cross > 0) 直接分支,漏掉浮点舍入后本该为正却算成负的情况。
立即学习“C++免费学习笔记(深入)”;
- 推荐 sign 函数:
int sgn(double x) { return (x > EPS) ? 1 : (x - 遍历时先计算
s = sgn(cross(...));首次遇到非零s时记下expected_sign = s;后续每个非零s都必须等于expected_sign - 若所有
s全为 0(所有点共线),不是多边形,应提前返回 false
完整可运行的 minimal 示例
以下代码假设顶点存于 vector<pair>></pair>,按逆时针或顺时针给定:
bool isConvex(const vector<pair<double,double>>& p) {
int n = p.size();
if (n < 4) return false;
int expected = 0;
for (int i = 0; i < n; ++i) {
auto& a = p[(i-1+n)%n];
auto& b = p[i];
auto& c = p[(i+1)%n];
double cr = (b.first - a.first) * (c.second - b.second)
- (b.second - a.second) * (c.first - b.first);
int s = sgn(cr);
if (s == 0) continue;
if (expected == 0) expected = s;
else if (s != expected) return false;
}
return expected != 0; // 至少有一个非零转向
}
关键细节:模运算 (i-1+n)%n 安全处理首尾索引;expected != 0 排除所有点共线情形;sgn 必须带 EPS。
容易被忽略的边界情况
真正卡住人的往往不是算法逻辑,而是输入质量与定义差异:
- 输入顶点若为顺时针序,叉积全为负——这仍是凸的,代码必须接受负号一致;不要硬性要求“必须为正”
- 四点中有三点共线(如矩形加一个中点)→ 构成退化四边形,严格数学定义下不算凸多边形(内角含 180°),你的需求是否允许?代码中
sgn==0被跳过,不影响判断,但需确认业务语义 - 顶点含重复点(如
p[0] == p[1])会导致叉积恒为 0,建议在调用前用unique预处理,或在叉积前加坐标校验 - OpenCV 或 Qt 等库的多边形函数可能默认要求逆时针序,而你的判断函数不关心方向——这点务必和上下游对齐
没做预处理的坐标序列,哪怕只差一个点顺序错位,整个判断就失效。别只盯着叉积公式,先盯住输入是不是真的“有序简单多边形”。


















