筛法求质数:埃氏筛,欧拉筛

对于整数而言:

前言

如何判断[2,n]之间每个数是质数还是合数呢?
首先我们可以枚举每一个数,用试除法判断它是质数还是合数。时间复杂度O( n n n \sqrt n nn )。
事实上还有更好的做法。

埃氏筛求质数

我们枚举到每一个数x,就从二倍开始枚举它的所有倍数{2x,3x,…},标记它的倍数,它的倍数一定不是质数。
如果枚举到一个数x时,它还没被标记,证明它在[2,x-1]范围内没有约数,也即它是质数。

其中vis[i]==true,表示i为合数:
在这里插入图片描述

这就是朴素的埃氏筛法。
3大概有 n 3 \frac n 3 3n个倍数,4大概有 n 4 \frac n 4 4n个倍数,i大概有 n i \frac n i in个倍数。
每个数i都会枚举它的所有倍数,也就是枚举倍数次。
时间复杂度为O( ∑ i = 2 n n i \sum_{i=2}^n{\frac n i} i=2nin)。
∑ i = 2 n n i \sum_{i=2}^n{\frac n i} i=2nin是一个调和级数,约等于O( n ln ⁡ n n\ln n nlnn)=O( n log ⁡ e n n\log_e n nlogen)=O(nlogn)。
时间复杂度也就是O(nlogn)
时间复杂度带有log是因为,一些数字被重复标记了。

事实上可以优化:
我们注意到,每个合数n都可以表示为 p ⋅ k p \cdot k pk的形式,其中p是n的一个质因子。
那么我们优化,只枚举质数的倍数,然后标记它。
在这里插入图片描述
很容易证明,这样筛的时间复杂度是O( n ln ⁡ ln ⁡ n n\ln\ln n nlnlnn)=O(nloglogn)。

这个就是埃氏筛(埃拉托色尼筛法)。

额外提一嘴,我们注意到,同一个数的相同质因子只算一次的话。[1,n]中所有数的质因子数目之和为O(nloglogn)。
因为我们注意到,在埃氏筛的过程中,每个数都被它的每个不同质因子筛到一次。

线性筛(欧拉筛)

我们注意到,即使是优化了之后的埃氏筛法也有算重的地方,因为一个数被它的所有不同质因子到。
如果想要获得更好的时间复杂度,我们只需要强制每一个数只被它的其中一个质因子筛到一次。
最简单的一种写法是,我们只让一个数被它的最小质因子筛到一次。

const int N=100000000;
bool vis[N+5];
vector<int> a;
void Euler(int n) {
	for(int i=2;i<=n;i++) {
		if(!vis[i])
			a.push_back(i);
		for(auto&j:a) {
			int x=i*j;
			if(x>N) break;
			vis[x]=true;
			if(i%j==0) break;
		}
	}
}

我们指定i*j的最小质因子必须为j。

如果i%j=0,表示i中含有j这个因子,这意味着 i × j i \times j i×j本身的最小质因子还是j,筛 i × j i \times j i×j本身是合法的。
但是下一次循环,由于质数是从小到大被筛出来的,也就是说质数j是升序存储的,j会变大,此时 新的 i × j i \times j i×j 的最小质因子就是i的最小质因子(也就是原来的j)了,违反了我们的规定,因此退出循环。

每个数都仅被它的最小质因子筛到一次。时间复杂度O(n)。

后记

于是皆大欢喜。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值