半素数是恰好有两个素因子(计重)的正整数,即Ω(n)=2;判断不能依赖因数个数,而需分解并统计素因子重数之和是否为2。

什么是半素数,以及为什么不能只看因数个数
半素数(semiprime)是指恰好能分解为两个素数乘积的正整数(这两个素数可以相同,比如 4 = 2 × 2)。注意:1 不是半素数,质数本身(如 5)也不是——它只有一个素因子,而半素数必须有且仅有两个素因子(计重),即 Ω(n) = 2(其中 Ω 是素因子计重函数)。
常见误区是用「因数个数是否等于 3 或 4」来判断,这不可靠:比如 8 = 2³ 有 4 个正因数(1,2,4,8),但它不是半素数;又如 6 = 2×3 有 4 个因数,却是半素数。关键不在因数个数,而在素因子的**重数和数量**。
最稳妥的做法:试除到 √n 并统计素因子重数
对给定正整数 n,遍历从 2 到 sqrt(n) 的所有整数 i,尝试整除,并在每次成功整除时检查 i 是否为素数(实际只需检查 i 是否为最小素因子即可,下文说明)。核心逻辑是:把 n 完全分解,记录每个素因子的出现次数,最后判断总重数是否恰好为 2。
- 先特判
n :直接返回 <code>false(最小半素数是 4) - 用变量
count = 0累计素因子重数 - 对每个
i从 2 循环到sqrt(n)(含),若n % i == 0,则:- 用内层循环不断除以
i,每除一次count++ - 若除完后
count > 2,可提前返回false - 同时确保
i是素数——但这里无需单独调用 is_prime():因为i是当前最小未被筛掉的因子,只要它能整除n,它必为素数(否则其素因子早已在更小的j 时被处理过了)
- 用内层循环不断除以
- 循环结束后,若
n > 1,说明剩余的n本身是一个未被除尽的大素因子,此时count++ - 最终返回
count == 2
示例:n = 15 → i=3 整除,15/3=5,count=1;再除不尽;循环结束,剩余 n=5>1,count=2 → true。n = 12 → i=2 整除,12→6→3,count=2;剩余 n=3>1,count=3 → false。
性能与边界要注意的几个点
该方法时间复杂度约为 O(√n),对 10⁹ 内的数足够快,但仍有几处易错:
i * i 比 <code>i 更安全,避免浮点误差和类型转换问题- 循环中每次
n /= i后,n变小,但上界i*i 不合适;正确做法是动态更新条件为 <code>i * i (因为剩余部分可能已变小) - 注意
int溢出:当n接近INT_MAX时,i * i可能溢出;建议用long long i或改写为i - 不要漏掉
n == 1的情况:此时 count 应为 0,不是半素数
一个简洁可用的 C++ 实现
下面是一个不依赖额外 is_prime 函数、兼顾正确性与可读性的版本:
bool isSemiprime(int n) {
if (n 2) return false;
n /= i;
}
}
if (n > 1) count++;
return count == 2;
}
这个函数能正确处理 4(2×2)、6(2×3)、9(3×3)、15、21、25 等,也拒绝 1、2、3、8、12、16、17 等非半素数。唯一隐含假设是输入为正整数;若需支持负数或 0,应先加校验。
真正容易被忽略的是:循环中 i 从 2 开始,但没跳过偶数之后的合数——这没关系,因为合数不可能整除当前的 n(它的素因子已被提前除尽),所以不会影响 count 的准确性,只是多做了几次取余判断而已。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











