阶乘数是指某个正整数n恰好等于某个整数k的阶乘(k!),判断逻辑为从小到大迭代计算k!并比对n,一旦k!超过n仍未匹配即返回false;需在每次乘法前用target/k防溢出,确保覆盖1!至20!且不误判。

什么是阶乘数?先确认判断逻辑
阶乘数是指某个正整数 n 恰好等于某个整数 k!(即 k 的阶乘)。比如 1、2、6、24、120 是阶乘数,分别对应 1!、2!、3!、4!、5!;而 5、10、100 不是。
关键点:C++ 中没有内置“反阶乘”函数,不能直接开根或取对数还原 k,必须靠枚举或迭代验证。因为阶乘增长极快,20! 已远超 uint64_t 范围(约 2.4e18),所以实际可检查的 k 很小(通常 ≤ 20)。
用循环递推计算并比对
最稳妥的做法是从小到大算 k!,每算一次就和目标值比较。一旦超过目标值还没匹配上,就说明不是阶乘数。
注意溢出风险:即使你用 unsigned long long,从 k = 1 开始累乘时,若目标值很大(如接近 ULLONG_MAX),中间乘法可能溢出,导致结果回绕为 0 或错误值,进而误判。必须在每次乘法前检查是否会溢出。
- 用
if (fact > target / k)判断下一步乘k是否会溢出(前提是target和fact都 > 0) - 起始设
fact = 1,k = 1,然后循环:while (fact - 循环内先检查
fact == target→ 是阶乘数;否则更新fact *= k++(但必须加溢出防护) - 退出循环后,再检查一次
fact == target,因为最后一次乘完可能刚好相等
为什么不用二分查找或数学近似?
有人想用斯特林公式估算 k 再验证,但浮点误差在小数值(如 1~1000)下不可靠,log(120) 算出来可能偏移 ±0.1,导致试错范围扩大;二分更没必要——总共最多试 20 次,线性遍历足够快且无边界误差。
常见错误现象:
- 直接写 for (int k = 1; k 然后算 <code>k! 存数组:看似简单,但没处理溢出,21! 在 unsigned long long 下会溢出成 0,后续比对全乱
- 用 double 算阶乘:double 在 22! 后就开始丢精度,25! 甚至无法精确表示整数部分
- 忘记 1 是 1! 也是 0!:题目限定“正整数”,所以只考虑 k ≥ 1 即可,0! 不参与判断
一个安全可用的参考实现
以下逻辑能正确处理最大到 20! = 2432902008176640000 的输入(ULLONG_MAX 约为 18446744073709551615,所以 21! 会溢出,必须拦截):
bool isFactorial(unsigned long long target) {
if (target == 1) return true; // 1! == 1
unsigned long long fact = 1;
for (int k = 2; ; ++k) {
if (fact > target / k) break; // 下一步会溢出,停止
fact *= k;
if (fact == target) return true;
if (fact > target) break;
}
return false;
}
这个版本不预存表、不依赖浮点、不假设输入范围,靠乘法前的除法检查防溢出,覆盖所有 unsigned long long 内合法阶乘数(1! 到 20!)。
真正容易被忽略的是:溢出检查必须用 target / k 而不是 ULLONG_MAX / k——因为只要当前 fact 已经大于 target / k,再乘 k 就一定超过 target,没必要等到溢出才停。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











