【质数】Eratosthenes埃拉托斯特尼筛法_欧拉筛法


前言:三种方法总述

埃氏筛和欧拉筛都具有极高的效率,其中,判断10^7个数欧拉筛只需0.9s,埃氏筛需1.2s,这两种筛法的思想都是将每一个数的倍数筛掉,所剩下的第一个数就一定是质数,而传统筛法就是将每一个数都找一遍因数,所以时间复杂度不堪入目。

一、传统筛法

1.原理

传统筛法就是以此判断范围内的数,每一个数都需从2开始判断是否可以整除,一直判断到根号n,若其中出现可以整除的情况,就可立即退出循环,若没有,说明此数是质数.

2.代码实现

以一道题做示例:区间内的质数:(求区间内的所有质数)

#include<bits/stdc++.h>
using namespace std;
int n,m,i,j;
bool s;
int main(){
	ios::sync_with_stdio(false);//读入优化
	cin>>m>>n;
	for(i=m;i<=n;i++){//i是当前整数
		for(j=2;j*j<=i;j++){//j用来判断是否是i的因数
			if(i%j==0) s=1;//如果j是i的因数,s变为一
		}
		if(s==0) cout<<i<<endl;//如果s=0,说明i是质数,输出;
		else s=0;//否则归0;
	}
}

因为此方法过于低级,这里就不在赘述了。

二、埃氏筛

筛质数是编程领域中常见的问题,很多信息学家都对其提出了见解,比如我们初学常用的线性筛法,还有方便一点的欧拉筛。这种筛法也叫埃氏筛,是由一个叫埃拉托斯特尼的大师发明的这种筛法的知名度没有那么高,而且原理和欧拉筛极其相似,很多人都误认为这就是欧拉筛,但其实这是两种不同的筛法。

我们先从质数本身入手

知己知彼,百战不殆。我们先要完全了解质数的性质,并且可以灵活运用才能达到目标。
质数就是不能被除1和他本身外任意一个正整数整除的数。那如何利用呢?

厄拉多塞筛原理

首先将2到n范围内的整数写下来,其中2是最小的素数。在表中将2的倍数划去,表中剩下最小的数字就是3,他不能被更小的整除,所以3是素数。再将表中所有3的倍数划去,依次类推,如果表中剩余最小的数是m那么m就是素数。然后将表中所有m的倍数划去,反复操作,依次枚举出n以内的素数。
首先,我们知道,如果能被2-n-1之内的数整除的都不是质数,反而言之,如果都不能被2-n-1之内的数整除的数都是质数,也就是厄拉多塞筛(以下简称厄筛)利用了这个原理,其精髓在于:


边遍历边筛


也就是说在遍历这个数时,已经有很多个数被同时筛掉了
效率极高

在理一下思路

首先,我们依然是从头遍历,但与其他筛法不同的是,我们并不是单一的只筛这个数,而是直接把其倍数筛去,也就是,比线性筛强的地方就在于:

线性筛需要从2开始遍历,而且要遍历n次,还要进行比较,而厄筛就只需要把质数的倍数筛掉,而合数只需要做O(1)的比较(就看bool类的数组里它的值为多少)我们知道合数比素数多得多,所以厄筛的效率有目共睹

code实现

#include<bits/stdc++.h>
using namespace std;
const int M=1e1+5;
int i,j,l,r;
bool pr[M]; 
int main(){
	ios::sync_with_stdio(false);
	cin>>l>>r;
	pr[1]=1;//注意!需要特判1
	for(i=l;i<=r;i++){
		if(pr[i]==1)//表示为合数,不用筛
			continue;
		else{//为质数
			for(j=2*i;j<=r;j+=i)//注意需要从2*i开始枚举
				pr[j]=1;
		}
	}
	for(i=l;i<=r;i++){
		if(pr[i]==0)//判断输出
			cout<<i<<" ";
	}
}
pr[1]=1;

这里一定注意要特判1,所以尽量在循环外就赋值为1

if(pr[i]==1)//表示为合数,不用筛
			continue;

这一步就是我们刚才所说的,bool类的值为1则表示他是合数,是合数为什么就不继续筛了呢,下面给出证明:

证明

因为我们知道,一个合数肯定有至少1个除1和它本身外的因数,那么可推出,这个数的倍数一定都是他因数的倍数,那么他的倍数既然已经被筛掉了,我们就不需要在花费时间了

else{//为质数
	for(j=2*i;j<=r;j+=i)//注意需要从2*i开始枚举
		pr[j]=1;//赋值为1
}

这一步就是在给是质数的数的倍数筛掉,注意是从2*i开始,不然他自己也被筛了,要知道他可是个质数。
其余就没有难点了,有还不懂的可以看注释,为了证明这个代码的正确性,这是输出的数据:
在这里插入图片描述
这种方法一做就对,时间又少,是不是简单易懂又方便呢!

三、欧拉筛

1.原理

将每一个数的倍数筛掉,所剩下的第一个数就一定是质数每一个合数都保证是被其最小的质因子筛去的;

2.代码实现

#include<bits/stdc++.h>
using namesapce std;
const int M=1e7+5;
int n,cnt,pr[M];//pr数组用来存放质数
bool p[M];
int main(){
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=2;i<=n;i++){
		if(p[i]==0)//判断质数
			pr[++cnt]=i,cout<<i<<endl;
		for(int j=1;(j<=cnt)&&(pr[j]*i<=n;j++){//枚举已有质数
			p[pr[j]*i]=1;
			if(i%pr[j]==0)//避免重复筛
				break;
		}
	}
}

总结

在这三种方法中,埃氏筛和欧拉筛都比较快,比较好用,但理解起来没有传统的容易,欧拉筛是在埃氏筛的基础上进行了优化,是一种线性筛法,这三种方法都是基于数学的,所以学好数学是编程的基础。

  • 6
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

来自八中的小鹿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值