半素数是恰好由两个质数(可相同)相乘得到的正整数,如4、6、9、15;判断时必须确保质因数总个数(含重)恰好为2,而非仅不同质因子个数为2或简单调用两次质数判断。

什么是半素数,以及为什么不能只看质因数个数
半素数是指恰好由两个(可以相同)质数相乘得到的正整数,比如 4(2×2)、6(2×3)、9(3×3)、15(3×5)。注意:12(2×2×3)不是半素数,因为它有三个质因子(计重);1、质数本身(如 5)也不是——前者无质因子,后者只有一个。
常见错误是写个函数统计质因数个数,然后判断是否等于 2。这会把 30(2×3×5)误判为半素数(它有 3 个不同质因子,但更关键的是质因子总个数(含重)是 3)。正确做法是:分解出所有质因子(含重),且总数必须恰好为 2。
暴力试除法判断半素数(适合 ≤10⁶)
对输入 n,从小到 2 开始试除,记录所有质因子(含重复)。一旦发现非质因子的合数被除尽,说明它本应被更小的质因子提前除掉,所以实际只需试到 sqrt(n) 即可。
- 从
i = 2开始循环,当i * i 时继续 - 若
n % i == 0,则i是质因子,计数器cnt++,并不断用i除n直到除不尽 - 循环结束后,若
n > 1,说明剩余的n本身是质数,再cnt++ - 最终返回
cnt == 2
示例逻辑:
立即学习“C++免费学习笔记(深入)”;
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
bool isSemiprime(int n) {
if (n < 4) return false; // 最小半素数是 4
int cnt = 0;
for (int i = 2; i * i <= n; i++) {
while (n % i == 0) {
cnt++;
n /= i;
if (cnt > 2) return false; // 提前剪枝
}
}
if (n > 1) cnt++;
return cnt == 2;
}为什么不能直接调用 is_prime 两次来判断
有人想:找一个质因子 p,再看 n/p 是否为质数。这看似简洁,但漏掉两种情况:
-
n是平方数,比如49→p=7,n/p=7是质数,没问题;但若你只试到sqrt(n)就停,可能错过p本身(比如n=4,i=2能整除,n/i=2是质数) - 更隐蔽的是:如果
n有超过两个质因子(如60 = 2×2×3×5),你随便挑一个质因子(比如2),60/2 = 30不是质数,于是你否定;但换一个因子(比如3),60/3 = 20也不是质数……你得穷举所有质因子才能确认,反而更慢
所以单次完整质因数分解比“枚举质因子 + 验证商”更可靠、更易剪枝。
大数(>10⁷)下的注意事项
如果 n 可能接近 10⁹,纯试除到 sqrt(n) 最坏要循环 ~31622 次,仍可接受;但若频繁调用,建议预处理小质数表(比如筛出 ≤10⁵ 的质数),用它们去试除,跳过合数,能快 3–5 倍。
另外,int 在某些平台可能溢出,对 >2³¹−1 的输入应改用 long long,且 i * i 要写成 <code>i 避免乘法溢出。
真正容易被忽略的点是:半素数定义要求“两个质数”,不是“两个不同质数”,所以 p² 类型必须支持——你的分解逻辑里不能跳过重复因子,也不能在找到第一个因子后就终止。

















