普通求素数是对每一个数的因子进行枚举,进而判断它是否是素数,但会很慢。
进阶一点的是埃式筛法,埃式筛法的基本思想就是,当我们遍历到一个素数时,把所有该素数的倍数(自然是合数)都筛选出来。但是这样同样会有重复筛选的情况而浪费时间,比如2的倍数有6,而3的倍数同样会把6,再次筛选一遍。
于是便有了欧拉筛来求素数的方法。
int prime[MAXN];
bool vis[MAXN];
int cnt=0;
void Euler_prime(int n) {
for(int i=2; i<=n; ++i) {
if(!vis[i]) {
prime[cnt++]=i; //vis[i]置为true或不置true都可以
vis[i]=true;
}
for(int j=0; j<cnt; ++j) {
if(i*prime[j]>n)//判断是否越界
break;
vis[i*prime[j]]=true;//筛数
if(i%prime[j]==0)//时间复杂度为O(n)的关键!
break;
}
}
}
在欧拉筛里面最为关键的一步便是if(i%prime[j]==0),而其最本质的思想是用最小质因数来筛掉一个合数。
首先可以知道的是一个正整数,如果它不是素数那么就一定会有素数因子。但是一个数的最小质因子肯定只有一个,那么就表明,我们一定可以用最小质因子来筛掉一个非素数。
证明:p j 一定是i的最小质因数。因为pj是质数且,如果 i 还有比pj更小的因子,由于p数组是有序的,所以会在更靠前的位置被枚举到。为了满足"于任意一个合数,它会且只会它的最小因数筛掉",所以小于等于pj的质数都可以被使用。而对于大于pj的质数,i里明显有着比pj更小的质数。
其次我们需要证明在一个合数被筛掉之前不会被当成素数纳入素数合集。这个合数k可以被表示为ipj,可以知道的是i和pj一定比k小,那么一定先别遍历到,那么就一定会被ipj筛掉。