三角数是形如n(n+1)/2的正整数(如1、3、6、10……);循环暴力判定最坏需o(√x)次迭代,对大数(如1e12)太慢;高效方法是解n²+n−2x=0,验证(−1+√(1+8x))/2是否为正整数,并用整数平方校验避免浮点精度误差。

什么是三角数,以及为什么不能用循环暴力判断
三角数是形如 n*(n+1)/2 的数(n 为正整数),比如 1、3、6、10、15……。如果用循环从 1 开始试算 i*(i+1)/2 直到 ≥ 目标值,最坏情况要迭代 O(√x) 次——对大整数(如 1e12)明显太慢。
真正高效的判定,是把问题反解:给定 x,是否存在正整数 n,使得 n*(n+1)/2 == x?整理得二次方程:n² + n − 2x = 0。解为 n = (−1 + √(1 + 8x)) / 2。只要这个结果是正整数,x 就是三角数。
用 sqrt 判定时必须处理浮点精度陷阱
double 的 sqrt 在大整数(如 > 2⁵³)上会丢失精度,导致 round 或 floor 出错。例如 x = 9999999999999999 时,sqrt(1+8*x) 可能返回略小或略大的值,直接转 int 就错。
- 先用
std::sqrt算出近似根root,再取long long r = std::llround(root) - 但必须验证:只接受满足
r*r == 1+8*x的r(用整数运算校验,不依赖浮点) - 然后检查
(r - 1) % 2 == 0且(r - 1) / 2 > 0,确保n是正整数
C++ 实现:安全、无分支、单次 sqrt
下面函数返回 true 当且仅当 x 是三角数:
bool isTriangular(long long x) {
if (x = 0 && s * s == disc) {
return (s - 1) % 2 == 0 && (s - 1) / 2 > 0;
}
}
return false;
}
关键点:
- 不依赖
sqrt的绝对精度,而是用整数平方校验 - 只做一次
sqrt,再在 ±1 范围内穷举——实际几乎总在第一次就命中 - 避免
sqrt对负数或溢出输入未定义行为(disc必须 ≥ 0,而x ≥ 1保证这点)
边界与性能注意点
对 long long 范围内的输入,disc = 1 + 8*x 最大可达约 8e18,仍在 double 可精确表示的整数范围内(≤ 2⁵³ ≈ 9e15)——但接近上限时已有风险。更稳妥的做法是用 std::sqrtl(long double 版本),或引入整数开方(如牛顿法),但绝大多数场景下上述 ±1 校验已足够健壮。
真正容易被忽略的是:x=0 和负数要提前排除;x=1 必须返回 true;而 disc 为完全平方数只是必要条件,还需确认 (r−1) 是偶数且为正——漏掉任一条件都会误判。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











