埃氏筛法和线性筛
埃氏筛法
如果一个数是合数,那么它一定有素数因子;
证明:反证法,如果一个数是合数,且其所有因子都是合数。 ----------------------(1)
如果(1)式正确,那么 4 为合数,其因子应为合数,但是 2 显然为素数,所有(1)式不成立;
埃氏筛法就是将 素数 的倍数全部筛掉
代码如下
const int N = 1e6 + 10;
int primes[N], ans; //全局变量 自动赋值为 0
bool st[N]; //全局变量 自动赋值为 false
void get_primes(int n) {
for (int i = 2; i <= n; ++i) {
if (!st[i]) {
primes[ans++] = i;
for (int j = i + i; j <= n; j += i) {
st[j] = true;
}
}
}
}
问题
看到这段代码,不知道你会怎么想,是不是也恰好有一下几个问题呢?
- 为什么 n 从 2 开始循环
- if 判断语句 () 里面的 !st [i] 是什么意思
- primes[ans++] = i 是什么意思
- 第九行 for 循环里面的 j 为什么初始化为 i+i ;
- 为什么 j += i
解答
- 因为 1 和 0 都不是素数;在判断之前,我们在初始条件的时候,默认的所有数都是素数;
- 因为我们默认所有数字都是素数,但是,bool 数组却默认初始化为 false ===> 假, 所有用取反 ! 来表示我们的原本的意思
- 如果这个数是素数,也就是这个数没被其他素数筛掉,我们就认为它是素数,就把他存到数组里面
-
- 这两条都是说,我们要把素数的倍数全部筛掉。
新问题
我们发现,埃氏筛法虽然无限逼近线性 O(n),但它却不是线性的,因为它还有可以 优化 的地方
- 当我们其中一个合数 6 进行分析的时候,我们不难发现,6 既被 2 筛一次,又被 3 筛一次;
- 如果继续观察其他合数,我们可以发现,大多数的合数的素数因子不唯一
- 那么问题来了,有没有一种方法可以让一个合数只被它的最小质因子筛掉呢?
答案是有的,线性筛!
在这之前,需要补充一下上面题解的 java 版本
class fun{
int[] primes = new int[1000010];
boolean[] st = new boolean[1000010];
int ans;
public void get_primes(int n){
for(int i=2; i<=n; ++i){
if(!st[i] ){
primes[ans++] = i;
for(int j=i+i; j<=n; j+=i){
st[j] = true;
}
}
}
}
}
线性筛
在阅读本部分之前,请确保你已经看懂了埃氏筛法
先上代码
const int N = 1e6 + 10;
int primes[N], ans;
bool st[N];
void get_primes(int n) {
for (int i = 2; i <= n; ++i) {
if (!st[i]) {
primes[ans++] = i;
}
//重点部分
//start
for (int j = 0; primes[j] <= n / i; ++j) {
st[primes[j]*i] = true;
if (i % primes[j] == 0) {
break;
}
}
//end
}
}
重点部分的理解
- j 为什么从 0 开始
- 为什么要 primes[j] <= n / i
- 这句话什么意思 *st[primes[j]i] = true
- 为什么要判断这句话 if (i % primes[j] == 0)
解答部分
-
因为我们要从最小的素数开始,将两数乘积的所有值都筛掉。之所以从 0,也是为了满足,每个合数只被它最小的质因子筛掉
-
这取决于我们筛数的范围 st[ ] 里面的值不难大于 n
st[primes[j]*i] = true
-
在这个问题上,我发现语言竟是如此的缺乏说服力,建议拿草稿纸演算一下。看看是否解决了 6 被 2 和 3 除两遍的问题
for (int j = 0; primes[j] <= n / i; ++j) { st[primes[j]*i] = true; if (i % primes[j] == 0) { break; } }
-
(1.)当 i % primes[j] == 0 成立, 则 primes[j] 一定是 i 的最小质因子;所有 primes[j] 也一定是 i * primes[j] 的 最小质因子
(2.)当 i % primes[j] == 0 不成立,则 primes[j] 一定是 i ***** primes[j] 的最小质因子,因为 j 是从 0 开始循环的;
那么为什么当判断语句成立的时候我们要跳出循环呢?
假设 i = x * primes[j] ,如果不跳出循环,则下一个被筛掉的数 primes[j+1] i 的最小质因子一定不是 primes[j+1] 而是primes[j]* ,就不符合条件。
java实现(面向数据编程)
class fun{
int cnt;
boolean[] st = new boolean[179424822];
public void get_prime(int[] prime){
for (int i = 2; i <= 179424821; i ++ )
{
if (!st[i]) prime[cnt ++ ] = i;
for (int j = 0; prime[j] <= 179424821 / i; j ++ )
{
st[prime[j] * i] = true;
if (i % prime[j] == 0) break;
}
}
}
}