这道OJ题目看似简单,实则暗藏玄机。作为计算机专业学生必练的基础算法题,"找出质数"考察的远不止循环和判断语句的使用。在实际开发中,质数判断算法广泛应用于密码学、哈希表设计等关键领域。本文将带你从暴力解法入手,逐步优化到筛法实现,并分享我在ACM竞赛中积累的质数判断技巧。
质数(Prime number)指在大于1的自然数中,除了1和它本身外不再有其他因数的数。理解这个定义时需要注意三个关键点:
最直观的判断方法就是试除法。对于一个待判断的数n,我们只需要用2到n-1之间的所有整数去试除n,如果都不能整除,则n是质数。用C++实现如下:
cpp复制bool isPrime(int n) {
if (n <= 1) return false;
for (int i = 2; i < n; i++) {
if (n % i == 0) return false;
}
return true;
}
这个实现虽然正确,但效率极低。时间复杂度是O(n),当n较大时(比如10^9量级),这个算法几乎无法在合理时间内完成。
观察质数的性质可以发现几个优化点:
优化后的算法实现:
cpp复制bool isPrime(int n) {
if (n <= 1) return false;
if (n == 2) return true;
if (n % 2 == 0) return false;
for (int i = 3; i * i <= n; i += 2) {
if (n % i == 0) return false;
}
return true;
}
这个优化将时间复杂度降到了O(√n),性能提升显著。对于n=10^9的情况,循环次数从10^9次降到了约3万次。
当需要找出一定范围内的所有质数时,筛法(Sieve)效率更高。埃拉托斯特尼筛法的基本思想是:
C++实现示例:
cpp复制vector<bool> sieve(int maxNum) {
vector<bool> isPrime(maxNum + 1, true);
isPrime[0] = isPrime[1] = false;
for (int i = 2; i * i <= maxNum; i++) {
if (isPrime[i]) {
for (int j = i * i; j <= maxNum; j += i) {
isPrime[j] = false;
}
}
}
return isPrime;
}
筛法的时间复杂度是O(n log log n),空间复杂度是O(n)。适合需要预处理质数表的场景。
东华OJ-101题通常要求:
结合上述分析,我们采用筛法实现:
cpp复制#include <iostream>
#include <vector>
using namespace std;
void printPrimes(int n) {
vector<bool> isPrime(n + 1, true);
isPrime[0] = isPrime[1] = false;
for (int i = 2; i * i <= n; i++) {
if (isPrime[i]) {
for (int j = i * i; j <= n; j += i) {
isPrime[j] = false;
}
}
}
for (int i = 2; i <= n; i++) {
if (isPrime[i]) {
cout << i << endl;
}
}
}
int main() {
int n;
cin >> n;
printPrimes(n);
return 0;
}
对于特别大的n(比如10^8),bool数组可能占用过多内存。可以采用位压缩技术:
cpp复制vector<uint8_t> isPrime((n + 7) / 8, 0xFF); // 每个字节存储8个标志位
void setNotPrime(int num) {
isPrime[num >> 3] &= ~(1 << (num & 7));
}
bool checkPrime(int num) {
return isPrime[num >> 3] & (1 << (num & 7));
}
这种方法可以将内存使用减少到原来的1/8。
当n极大时(比如10^12),无法一次性加载到内存。可以采用分段筛法:
现代CPU多核心,可以将筛法并行化:
如果程序超时,检查:
对于大n,可能出现内存不足。解决方案:
质数判断算法在以下领域有重要应用:
在ACM竞赛中处理质数问题时,我总结了几点经验:
预处理是关键:如果题目允许预处理,先筛出可能用到的质数表
空间换时间:在内存允许的情况下,预计算可以大幅提升运行速度
注意数据范围:不同范围的n适用不同算法
测试用例要全面:特别关注边界值(0,1,2,偶数,大质数等)
最后分享一个实用技巧:在C++中,可以用bitset代替vector
cpp复制bitset<10000001> isPrime; // 声明时大小必须为常量
isPrime.set(); // 全部初始化为1