线性筛一般是接近线性的速度去进行素数筛,所以时间接近O(n)
而线性筛的原理是处理掉埃筛的不完美问题,即使被重复筛选过的数只被筛选过一次
而每个大于等于2的数都具有的特性是每个数都可以分解为n个素数相乘,即唯一分解定理。
结合代码实际来看:
const int N = 20010;
int cnt = 0;//素数个数
int prime[N];//素数集合
bool ispri[N] = {true, true};//素数的判定集合,0和1不是素数
void EulerPrime(){
for(int i = 2; i < N; i++){
//如果是素数,放入素数集合中
if(!ispri[i])
prime[cnt++] = i;
//j < cnt即保证i从prime[0]乘到prime[cnt - 1]的数
//i * prime[j]即保证数不超过筛选的范围
for(int j = 0; j < cnt && i * prime[j] <= N; j++){
//将(i包括其自己之前的所有素数)都与 它 相乘得到的值置为true,必定为合数
ispri[i * prime[j]] = true;
//每个数只被它的最小质因数的筛一次,当这个数可以整除素数集合中的某个数时,说明这个数中已取到了最小质因子
if(i % prime[j] == 0)
break;
}
}
}
只看代码不太好理解,我们下面从具体例子来分析
我们将ispri[i * prime[j]] = true;当i % prime[j] == 0时,跳出本次循环,
即我们可以得知之后的prime[j]就不会是i * prime[j]的最小质因子了。
当i = 2时,prime[] = {2}; ispri[2 * 2] = true; 2 % 2 == 0,跳出循环
当i = 3时,prime[] = {2, 3}; ispri[3 * 2] = true, ispri[3 * 3] = true;
当i = 4时,prime[] = {2, 3}; ispri[4 * 2] = true; 4 % 2 == 0,跳出循环
当i = 5时,prime[] = {2, 3, 5}; ispri[5 * 2] = true, ispri[5 * 3] = true, ispri[5 * 5] = true;
当i = 6时,prime[] = {2, 3, 5}; ispri[6 * 2] = true; 6 % 2 == 0,跳出循环
当i = 7时,prime[] = {2, 3, 5, 7}; ispri[7 * 2] = true, ispri[7 * 3] = true, ispri[7 * 5] = true, ispri[7 * 7] = true;
当i = 8时,prime[] = {2, 3, 5, 7}; ispri[8 * 2] = true; 8 % 2 == 0,跳出循环
当i = 9时,prime[] = {2, 3, 5, 7}; ispri[9 * 2] = true, ispri[9 * 3] = true; 9 % 3 == 0,跳出循环
通过以上的例子,会发现 i * prime[j] 的最小质因子一定存在于i中
即i = p1^x1 * p2^x2 *...*pn^xn(pi是素数且i < j则pi < pj)
所以我们遇到i % prime[j] == 0的时候,继续往下筛,i * prime[j + 1] 中的最小质因子就会存在于i中了,那么i * prime[j + 1]这个数就会被筛至少两次了。
例如上例,当i = 4时,prime[] = {2, 3}; ispri[4 * 2] = true; 4 % 2 == 0
那么如果我们继续往下筛,得ispri[4 * 3] = true;那么这时候最小质因子就不是prime[j]了,那么当i = 6时,ispri[6 * 2] = true; 这时候就被重复筛了一遍,所以就不符合我们所要求的所有数被筛一遍(被其最小质因子筛一遍)即可。
所以当某个prime[j]可以整除i的时候,那么它之后的素数一定就不会是i * prime[j]最小质因子,这时候就可以退出,进行下一个数的素数筛。