三角数是形如n(n+1)/2的正整数(如1、3、6、10…),不能用循环累加判断因其最坏时间复杂度为o(√x),对大数(如1e12)效率低且易溢出;应解二次方程n²+n−2x=0,验证(−1+√(1+8x))/2是否为正整数,并通过整数平方校验避免浮点误差。

什么是三角数,以及为什么不能用循环累加判断
三角数是形如 1, 3, 6, 10, 15… 的数,即第 n 个三角数为 T(n) = n*(n+1)/2。如果对每个输入都从 1 开始循环累加直到 ≥ 目标值,最坏情况要跑 O(√x) 次——对 10¹² 级别的数就很慢,且容易溢出或超时。
数学上可逆:若 x 是三角数,则存在正整数 n 满足 n*(n+1)/2 == x,整理得二次方程 n² + n − 2x = 0。解出正根 n = (−1 + √(1 + 8x)) / 2,只需验证该 n 是否为正整数即可。
用 sqrt 和整数判定完成极速判断(C++ 实现)
关键不是算出精确 n,而是避免浮点误差导致误判。直接对 1 + 8*x 开方后取整,再反代验证更可靠。
- 先检查
x是否为正整数(输入已保证,但逻辑上需明确) - 计算
long long discriminant = 1 + 8LL * x,必须用8LL防止int溢出 - 用
sqrt(discriminant)得到浮点结果,转long long向下取整得root - 验证是否满足
root * root == discriminant(排除非完全平方数) - 再验证
(root - 1) % 2 == 0且(root - 1) / 2 > 0,即n = (root−1)/2是正整数
示例代码片段:
bool isTriangular(long long x) {
if (x 0;
}
为什么必须校验 root * root == disc 而不直接信 sqrt
sqrt 返回 double,对大整数(如 > 2⁵³)无法精确表示所有整数,sqrt(1 + 8*x) 可能被舍入成略小或略大的值。比如当 disc = 999999999999999999,sqrt 结果可能显示为 999999999.9999999,强制转 long long 就变成 999999999,而真实平方根是整数 1000000000。
所以必须用整数方式校验:root 取整后,立刻检查 root*root 和 root*root > disc 两种边界,必要时自增一次再判——这是唯一绕过浮点陷阱的稳妥做法。
边界与溢出处理要点
输入是“正整数”,但没说范围。若用 int 接收 x,则最大只支持约 2×10⁹;而 1 + 8*x 在 x ≈ 2.7×10¹⁸ 时就会溢出 unsigned long long。实际中应:
- 输入类型统一用
long long(或uint64_t) - 在计算
8LL * x前,可先判断x > ULLONG_MAX / 8(若需防御性编程) - 对极大
x(如 > 1e18),sqrt在double下精度不足,此时应改用sqrtl(long double)或整数开方算法 - 多数 OJ 场景下
x ≤ 1e12,double完全够用,但校验步骤不可省
真正容易被忽略的,是那个“多试一次 root+1”的校验动作——它看起来多余,却是浮点到整数映射时唯一兜底手段。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











