返回
Featured image of post 欧拉筛-高效筛选质数

欧拉筛-高效筛选质数

如何高效筛素数?让我们来看看

题目:生成一个1到n范围内的质数数组

菜鸟这样做(枚举法)

从1枚举到n,判断是素数就存入数组

下面是判断方法

菜鸟:

从2枚举到n-1,如果有能整除该数的,该数就是质数。

进化版菜鸟:

菜鸟进化了,从2枚举到$\sqrt n$


高手版

埃氏筛

这样筛掉后每次下一位没被筛掉的数就都是质数

bool numlist[100000];
//0代表质数,1代表非质数
int prime[100000], count;
void work(int n){
	for(int i=2; i<=n; i++){
		if(numlist[i]==0){  //如果是质数
			prime[++count] = i ;  //就先存入
			for(int j=i; i*j<=n; j++) numlist[i*j] = 1;  //然后将其平方后的所有倍数筛掉
		}
	}
	return;
}

但是这钟筛法还不是最优,因为p的倍数会重复,所以有些数会被重复筛掉。

那么就引出了我们的最终筛法。

欧拉筛

这个筛法避免了重复筛,思路就是:每个合数只会被其最大非自身因数筛除

多说无益,先上代码:

bool numlist[100000];
//0代表质数,1代表非质数
int prime[100000], count;
void work(int n){
	for (int i = 2; i <= n; i++) {
		if (numlist[i]==0) ans[++count] = i;//是质数就先存入数组
		for (int j = 1; j <= count && i*prime[j] <= n; j++) {
			numlist[prime[j] * i] = 1; //每次让i乘已放入的质数,然后标记非质数
			if (i % prime[j] == 0) break;//但是如果i可以被该数整除就停止(关键)
		}
	}

现在我们再来分析下。

从表可以看出,我们会筛掉每个质数p的平方之后的p的倍数,但是如果i是p的倍数,那就说明它们的乘积那个合数的最大非自身因数不是i,所以不筛。

i 的值质数表筛去的数
224
32, 36, 9
42, 38
52, 3, 510, 15, 25
62, 3, 512
72, 3, 5, 714, 21, 28, 35