合数的阶乘是指某个合数k(即大于1且非质数的正整数)的阶乘k!;判断一个正整数n是否等于某个合数的阶乘,需检查n是否恰好等于4!、6!、8!、9!、10!、12!、14!、15!、16!、18!或20!中的某一个,因20!已接近unsigned long long上限,更大合数阶乘必然溢出或远超n。

什么是合数的阶乘,先搞清定义
合数是指大于1且不是质数的正整数(即至少有三个正因数),比如 4、6、8、9、10……它们的阶乘分别是 4!、6!、8! 等。注意:题目不是“判断一个数是否为某个合数的阶乘”,而是“判断它是否等于某个合数的阶乘”——也就是说,给定一个正整数 n,要确认是否存在某个合数 k,使得 k! == n。
直接暴力枚举合数并计算阶乘最可靠
阶乘增长极快,10! 就已达到 3,628,800,12! 是 479,001,600,15! 已超 1.3×10¹²。对 unsigned long long(通常上限约 1.8×10¹⁹)来说,最多只需试到 20!(≈2.43×10¹⁸),再往上就溢出了。而小于 20 的合数只有:4、6、8、9、10、12、14、15、16、18、20 —— 共 11 个值。
所以实际做法就是预计算或在线计算这些合数的阶乘,与输入比对:
- 从
k = 4开始,每次递增,跳过质数(可用简单试除法快速判质,或直接硬编码合数列表) - 用
unsigned long long累乘计算k!,一旦乘积 >n就停止(避免溢出和无谓计算) - 若某次结果恰好等于
n,返回 true;遍历完所有 ≤20 的合数都没匹配,就返回 false - 特别注意:
n = 1不是任何合数的阶乘(最小是4! = 24),n 可直接返回 false
别用二分查找或数学反解,容易翻车
有人想先求 n 的“阶乘根”(即找 k 使得 k! ≈ n),再检查 k 是否为合数。这看似高效,但问题很多:
-
log(n)或斯特林公式估算k会有浮点误差,尤其对中等大小的n(如1000000),算出的k可能偏小或偏大 1 - 即使得到候选
k,仍需验证k!是否真等于n,而计算k!本身就要 O(k) 时间 —— 和直接枚举没本质区别 - 更麻烦的是:如果
n溢出unsigned long long(比如输入是字符串或__int128),那所有基于整型阶乘的算法都要重写;此时硬编码查表反而更稳
边界与溢出必须显式处理
阶乘计算中,unsigned long long 溢出是未定义行为(UB),不能依赖 if (fact * (k+1) 这类检测(编译器可能优化掉)。稳妥做法是:
- 每次乘法前检查:
if (fact > ULLONG_MAX / (k+1))→ 溢出,break - 或者用
std::mul_overflow(C++23):if (!std::mul_overflow(fact, k+1, &fact)) break; - 输入
n若为字符串(比如长度 > 20 位),说明它远大于20!,可直接返回 false(因为最大合法合数阶乘是20!) - 别忘了 4 是最小合数,
4! = 24,所以n 一律 false;<code>n == 24就是答案
真正难的不是逻辑,是把溢出检查写对、把合数列表列全、把 unsigned long long 的上限心里有数。其余都是体力活。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











