线性筛素数
代码实现:
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的数,依次类推,可以把所有的合数都筛掉
因为是按照最小素因子筛选,所以可以保证每个数都只会被筛一遍
另一个证明:
参考:https://blog.csdn.net/leolin_/article/details/6642126
原理:
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时才检查