线性筛素数

代码实现:

typedef long long ll;     
const ll N = 200000;
ll prime[N] = {0},num_prime = 0; //prime存放着小于N的素数
int isNotPrime[N] = {1, 1}; // isNotPrime[i]如果i不是素数,则为1
int Prime()
{
for(ll i = 2 ; i < N ; i ++)
{
if(! isNotPrime[i])
prime[num_prime ++]=i;
//无论是否为素数都会下来
for(ll j = 0 ; j < num_prime && i * prime[j] < N ; j ++)
{
isNotPrime[i * prime[j]] = 1;
if( !(i % prime[j] ) ) //遇到i最小的素数因子
//关键处1
break;
}
}
return 0;
}

基本思想

1.

如果 i 都是是素数的话,那简单,一个大的素数 i 乘以不大于 i 的素数,这样筛除的数跟之前的是不会重复的。筛出的数都是 N=p1*p2的形式, p1,p2之间不相等

2.
当前数字i是合数,则​​​i=p1^a * p2^b * p3^c​​(p1<p2<p3且均为素数),一次循环筛除小于等于p1的素数乘以i得到的数。比如p1之前有pi,pj和pk三个素数,则此次循环筛掉​​pi*i,pj*i,pk*i​​​和​​p1*i​​,实现见代码的关键一,​​prime​​​ 里的素数都是升序排列的,​​break​​​时的遇到能整除的第一个​​prime[j]​​​ 就是这里的​​p1​​。

结论:

1.一个数肯定不会被重复筛除

2.合数肯定被重复筛除。

关键:按照一个数的最小素因子筛选

图解:

参考:https://www.jianshu.com/p/f16d318efe9b

线性筛素数 证明详解_筛素数


从图上我们看到,第一列筛掉的是最小素因子是2的数,第二列筛掉的是最小素因子为3的数,依次类推,可以把所有的合数都筛掉

因为是按照最小素因子筛选,所以可以保证每个数都只会被筛一遍


原理:
1. 任何一个合数都可以表示成一个质数和一个数的乘积
2. 假设A是一个合数,且A = x * y,这里x也是一个合数,那么有:
       A = x * y; (假设y是质数,x合数)
       x = a * b; (假设a是质数,且a < x——>>a<y)
 ->  A = a * b * y = a * Z (Z = b * y)
即一个合数(x)与一个质数(y)的乘积可以表示成一个更大的合数(Z)与一个更小的质数(a)的乘积!!!!
这也是理解代码中 if(i%primes[j] == 0)break;的关键
例如: 如果i = 8; 那么由于i%2 == 0; 因此对于i=8就只需要检查primes[1]=2即可,因为对于大于primes[1]的质数,像3,有:
        8*3 = 2*4*3 = 12*2
也就是说24(8*3=24)并不需要在8时检查,在12时才检查