欧拉筛很简单,所以文章很短。
核心思想:每个数尽量只被筛一次
算法核心要点:
筛法,顾名思义,筛去所有不合法的,留下得就是合法的。
要筛出来素数,就是要筛去所有合数。
为了尽量不重不漏的枚举合数,我们就要发现每个合数唯一的特征来做到去重,
(这里“唯一的特征”在去重的思想中很常见)
于是以每个合数最小的素因数为这个特征,因为显然的,每个合数都有且只有唯一的一个“最小的素因数”。
那么每个合数都可以被唯一地分解成它最小的素因子×一个大于一且小于自己的数
形式化的,如果$m$是合数,那么$m$一定可以被唯一的分解成$p \times i$($p \in \mathbb{P},1<i<m$ ),其中$p$小于等于$i$的所有素因子。
我们如果能保证枚举的$p$和$i$不重复,那么就可以保证每个数都只被筛一次。
于是我们枚举$i$,再枚举对于$i$所有合法的$p$,就能枚举出所有的合数$m$,做到线性筛。
int prime[MAXN],tot=0;bool vis[MAXN];
void Euler_sieve(int n){
for(int i=2;i<=n;i++){// ......(1)
if(!vis[i])prime[++tot] = i;//没被标记过,是素数
for(int j=1;j<=tot;j++){
if(i * prime[j] > n)break;//不用筛界外的数
vis[i * prime[j]] = true;// ......(2)
if(i % prime[j] == 0)break;//已经枚举到i的最小素因子了,剩下的以后会筛到
}
}
return ;
}
/*
复杂度证明:
(1)代码中第一层循环将 每个数 至少访问一遍
(2)第二层循环会不重复的把每个 合数 多标记一遍
于是复杂度为 O(c * n) , 1 < c < 2 。
*/