三角形数是能排成等边三角形点阵的正整数,第n个为T(n)=n(n+1)/2;x是三角形数当且仅当1+8x为完全平方数且其平方根为奇数。

什么是三角形数?直接看判定公式
三角形数是能排成等边三角形点阵的正整数,比如 1、3、6、10、15……对应第 n 个三角形数为 T(n) = n*(n+1)/2。反过来看:给定一个正整数 x,它是否等于某个整数 n 对应的 T(n)?
代数上可推得:若 x = n<em>(n+1)/2</em>,则整理为二次方程 n² + n − 2x = 0,解得n = (−1 + sqrt(1 + 8x)) / 2。
所以 x 是三角形数 ⇔ 1 + 8*x 是完全平方数,且该平方根为奇数(保证分子为偶数,除以 2 后得整数 n)。
用 sqrt 判定时为什么不能直接比 ==
C++ 中 sqrt 返回 double,浮点计算有精度误差。例如对大整数 x = 1e12,1 + 8*x 是精确整数,但 sqrt 可能返回略小于真实整数值的浮点数(如 2828427.9999999995),强制转 long long 就会截断成 2828427,导致误判。
- 用
llround(sqrt(val))替代直接static_cast<long long>(sqrt(val))</long> - 计算
root = llround(sqrt(1 + 8.0 <em> x))</em>后,必须验证root root == 1 + 8 * x(用整数运算校验) - 不要依赖
sqrt的小数部分是否“接近 0”,只信整数平方校验
完整可移植的判定函数怎么写
以下函数适用于 unsigned long long 范围内(约 ≤ 1e19),不依赖 __int128 或第三方库:
bool isTriangular(unsigned long long x) {
if (x == 0) return false; // 题设要求正整数
unsigned long long val = 1 + 8 * x;
unsigned long long root = llround(sqrt((double)val));
// 防御性校验:root 可能因 double 精度溢出而错误,先限幅
if (root > 3037000500ULL) root = 3037000500ULL; // sqrt(UINT64_MAX) ≈ 4.29e9,这里取保守上界
if (root * root != val) return false;
return (root & 1) == 1; // root 必须是奇数,才能让 (-1 + root) / 2 为整数
}-
llround在<cmath>中,需链接-lm(Linux) - 对超大
x(如 > 1e15),(double)val可能无法精确表示整数 → 此时应改用整数开方(如牛顿法),但日常场景中double能精确表示到 2⁵³ ≈ 9e15,已覆盖多数需求 -
(root & 1) == 1比root % 2 == 1更快,也避免负数问题(此处 root ≥ 0)
容易被忽略的边界和性能点
-
x = 1 是合法三角形数(对应 n = 1),函数必须返回 true;漏掉这个会暴露逻辑漏洞
- 当
8<em>x</em> 溢出 unsigned long long 时,1 + 8x 会回绕——应在计算前加范围检查:if (x > ULLONG_MAX / 8) return false;
- 若需高频调用(如筛 1e6 个数),预计算三角形数集合(用
unordered_set)可能比每次开方更快,但空间换时间,得看场景
- 不要用
float,它只有 24 位有效精度,x > 2²⁰ ≈ 1e6 就可能出错
x = 1 是合法三角形数(对应 n = 1),函数必须返回 true;漏掉这个会暴露逻辑漏洞 8<em>x</em> 溢出 unsigned long long 时,1 + 8x 会回绕——应在计算前加范围检查:if (x > ULLONG_MAX / 8) return false; unordered_set)可能比每次开方更快,但空间换时间,得看场景 float,它只有 24 位有效精度,x > 2²⁰ ≈ 1e6 就可能出错 数学公式很干净,落地时每一步浮点转换、整数溢出、奇偶校验都得亲手掐住。
立即学习“C++免费学习笔记(深入)”;


















