1. 初识素数筛法:从暴力枚举到埃氏筛
第一次接触素数筛选算法时,很多人都会从最朴素的暴力枚举开始。比如判断数字n是否为素数,就逐个检查2到√n之间的整数能否整除n。这种方法在小范围内勉强可用,但当需要找出1到100万之间的所有素数时,效率就低得令人难以忍受了。
埃拉托斯特尼筛法(简称埃氏筛)的出现,让素数筛选效率提升了一个数量级。记得我第一次实现埃氏筛时,被它的简洁性惊艳到了——只需要一个布尔数组和两个嵌套循环,就能高效筛选素数。核心思想就像筛沙子:先把所有数标记为"可能是素数",然后从小到大,遇到素数就筛掉它的所有倍数。
// 经典埃氏筛实现
const int MAX = 1e6;
bool is_prime[MAX];
void eratosthenes() {
memset(is_prime, true, sizeof(is_prime));
is_prime[0] = is_prime[1] = false;
for (int i = 2; i*i < MAX; i++) {
if (is_prime[i]) {
for (int j = i*i; j < MAX; j += i) {
is_prime[j] = false;
}
}
}
}
这个算法有两个精妙之处:一是从i²开始筛,因为更小的倍数已经被更小的素数筛过了;二是外层循环只需要到√n,因为更大的数的倍数都会超出范围。但实际使用中我发现,当处理1亿以上的数字时,埃氏筛开始显现出明显的性能瓶颈。
2. 埃氏筛的局限性与重复筛选问题
在项目实践中,我发现埃氏筛最大的问题是会重复标记合数。比如数字12会被2和3各标记一次,30会被2、3、5各标记一次。这种重复操作随着数据规模增大而愈发严重,导致时间复杂度虽然是O(n log log n),但常数项很大。
举个具体例子:当筛选1到100的数字时:
- 数字6被


1883

被折叠的 条评论
为什么被折叠?



