题目
给出区间[1,n],统计其间的素数个数
n<10^8
思路
埃氏筛法
最小的素数是2,那么2的整数倍都不是素数,删去4,6,8…
余下的数里,最小的素数是3,删去6,9,12…
最终未被删去的数就是素数
但,这样在效率上有一个问题:一个数会被删去多次
例如42会被2 3 7都删去一遍
其时间复杂度为O(NlogNlogN)
for( int i=2;i<=n;i++ )
if( !death[i] ) {
death[i]=1;
for( int k=2;k*i<=n;k++ )
death[k*i]=1;
}
欧拉筛法
对每个合数a×b,它会被每个质因数都筛去一遍
但我们只要用最小的质因数筛去就好了
为此,我们需要记录下所产生的全部素数,代码如下
for( int i=2;i<=n;i++ ) {
if( !death[i] )
primelist[++tail] = i;//record this new prime
for( int k=1;k<=tail;k++ ) {
if( primelist[k]*i > n ) break;
death[primelist[k]*i] = 1;
if( i%primelist[k] == 0 ) break;//!!!
}
}
核心就是
if(i%primelist[k]==0) break;
如果i能整除primelist[k],
说明primelist[k]是i的因子,
所以primelist[k]也是i的任意倍数的因子。
所以primelist[k]也是i×primelist[x] (x>k)的因子。
考虑到primelist单增,对i×primelist[x],primelist[k]就是它的比primelist[x]更小的因子。
故不用考虑其后的质因子了。
(i×primelist[x]会被primelist[k]作为因子在i更大时被筛掉)