质数

1.质数

若对于一个大于 \(1\) 的正整数 \(x\),有:

\(\forall1< y< x ,y\nmid x\)

则称此数为质数(素数),否则此数为合数

判定一个质数的方法之试除法:检查 \(2\sim \sqrt{n}\) 之间的所有数是否能整除 \(n\)

bool check(int x) {
	int cnt=sqrt(x);
	for(int i=2;i<=cnt;++i) {
		if(x%i==0) return false;
	}
	return true;
}

\(1\sim n\) 之间所有的质数:筛法

暴力

\(1\sim n\) 的所有数判断是否是质数。

时间复杂度 \(O(n\sqrt{n})\)

朴素筛法

扫描 \(1\sim n\),标记 \(n\) 以内所有的倍数,若当前没被标记则为质数。

调和级数知时间复杂度约为 \(O(n\log n)\)

埃拉托斯特尼筛法(埃氏筛)

考虑优化上述筛法,发现只要标记质数的倍数就行。

并且由于 \(1\sim n\) 的数只需扫描 \(1\sim \sqrt n\) 的数即可全部标记。

时间复杂度趋近于 \(O(n\log\log n)\)

int vis[N],n;
vector<int>prime;
for(int i=2;i*i<=n;++i) {
	if(vis[i]) continue;
	for(int j=i*i;j<=n;j+=i) vis[j]=1;
}
for(int i=1;i<=n;++i) if(!vis[i]) prime.push_back(i);

时间复杂度证明。

线性筛法(欧拉筛)

再次考虑优化,发现会重复标记很恶心。

考虑如何仅标记一次,直接被最小质因子标记一次即可。

具体见注释。

int v[N],n;
vector<int>prime;
for(int i=2;i<=n;++i) {
	if(!v[i]) v[i]=i,prime.push_back(i);//是质数。 
	for(int p:prime) {
		if(p*i>n||v[i]<p) break;//超过范围或者有更小质因子。 
		v[p*i]=p;
	}
}

找大于 \(x\) 的最小的质数/小于 \(x\) 的最大的质数:

筛出符合范围的所有质数,再二分查找。

但真的很优秀吗?若至算法一个!

素数密度:在 \(1\sim n\) 中约有 \(\frac{n}{\ln n}\) 个质数,即每 \(\ln n\) 个数中大约有一个质数。

根据性质,我们直接暴力向前/后扫,试除法判断扫到的数是不是质数。

时间复杂度为 \(O(\sqrt n \log n)\)

区间筛法

\(Q:\) 给定 \(l,r\),求区间 \([l,r]\) 以内的所有质数。

\(l,r\le 1e9,r-l\le 1e6\).

暴力筛 \(r\) 以内所有质数不现实。

发现 \([l,r]\) 以内的所有数只需用埃氏筛标记 \(1\sim \sqrt r\) 中的质数即可将质数全部筛出。

所以用偏移量数组,按照普通埃氏筛即可。

复杂度 \(O(m\log\log m)\),其中 \(m=r-l\)

void Eratosthenes(ll n){
	for(ll i=2;i*i<=n;i++)
		for(ll j=(l+i-1)/i*i;j<=n;j+=i)
			if(j!=i)b[j-l]=0;
}
int main() {
	scanf("%lld%lld",&l,&r);
	Eratosthenes(r);
	for(int i=l;i<=r;i++)
        if(b[i-l]) ans++;
    ans-=max(2-l,0);
	printf("%lld\n",ans);
	return 0;
}
posted @ 2024-07-17 01:07  cannotmath  阅读(37)  评论(0编辑  收藏  举报