埃式&欧拉筛&大区间素数筛

本文详细介绍素数筛法的基本概念及其多种实现方式,包括普通筛法、埃式筛法的优化版本及高效的欧拉筛法,并提供大区间内求素数的方法与代码示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

素数筛

1.定义

前n个数有多少是素数(n<=1e6到7)。
并且用prime[]存素数。
时间复杂度O(nloglogn).

2.感悟

任何一个数都可以由若干个素数乘积表示。
eg: 100= 2* 2 * 5 * 5
2.一个数n=a*b.那么a<sqrt(n),b>sqrt(n).

3.普通素数筛

#include<iostream>
using namespace std;
const int maxn=1e7; //仅仅限于1e7,太大就超时。 
int prime[maxn]; //存放素数 
int visit[maxn+1]; //标记,2,4,6,8,10,12... 3,6,9,12,15... 
int E_sieve(int n){  //前n,有多少个素数。 
	int k=0;
	for(int i=2;i<=n;i++){
		if(!visit[i]){
			prime[++k]=i; //为什么i就是素数呢
			//因为前(i-1)项已经被筛掉了。 
			for(int j=2*i;j<=n;j+=i){  
				visit[j]=1; //筛掉 
			}
		}
	}
	//for(int i=1;i<=k;i++) cout<<prime[i]<<" ";cout<<endl;
	return k;
}
int main (){
	int n;
	cin>>n;
	cout<<E_sieve(n);
}

上面的两重for循环有这样的特性:

2* 2; 2* 3 ; 2* 4; 2* 5; 2* 6; 2* 7; 2* 8;…
3* 2 ; 3* 3 ; 3* 4; 3* 5 ; 3* 6; 3* 7; 3* 8 ;…
4* 2 ;4* 2; 4* 3; 4* 4; 4* 5; …
在上面有些乘法就重复了,比如:2* 3 ;3* 2;

2* 2; 2* 3 ; 2* 4; 2* 5; 2* 6; 2* 7; 2* 8;…
3* 3 ; 3* 4; 3* 5 ; 3* 6; 3* 7; 3* 8 ;…
4* 4; 4* 5; 4* 6; 4* 7; 4* 8;…
这样的就没重复乘法的结果了
下列优化:

4.对埃式素数筛优化 O ( n l o g ( n ) ) O(nlog(n)) O(nlog(n));


//重复乘的除。

int E_sievepro(int n){
	int k=0;
	for(int i=0;i<=n;i++) visit[i]=false;
	//其原理和试除法一样:非素数k必定可以被一个小于等于sqrt(k)的素数整除。
	//所以是i*i<=n, 在这里面一个合数被筛掉绝对被i整除. 
	for(int i=2;i*i<=n;i++){
		if(!visit[i]){
			/*再优化 
			j=2*i优化为j=i*i; 
			eg: i=5时,2*5,3*5,4*5....已经在i=2,3,4筛过.
			i=2时  4,6,8,10,12....2*k k=,2,3,4,5..... 
			i=3 时 9,12,15,18...   
			*/ 
			for(int j=i*i;j<=n;j+=i){
				visit[j]=true;  
			}
		}
	}
	for(int i=2;i<=n;i++){
		if(!visit[i]) prime[++k]=i;
	}
   //for(int i=1;i<=k;i++)
    //cout<<prime[i]<<" "; cout<<endl;
    return k;
} 

5. 欧拉筛 o ( n ) o(n) o(n)

时间复杂度:o(n)

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(int i=a;i<=b;i++)
ll gcd(ll a,ll b){ return b? gcd(b,a%b):a;}
const int N=2e6+10;
const ll mod=998244353;
ll read(){
    ll s = 0, f = 1; char ch = getchar();
    while(!isdigit(ch)){
        if(ch == '-') f = -1;
        ch = getchar();
    }
    while(isdigit(ch)) s = (s << 3) + (s << 1) + (ch ^ 48), ch = getchar();
    return s * f;
}
using namespace std;
// phi是φ(i), v[i]:i的最小质因数  prime[i]:第几个质数 
int phi[N],v[N],prime[N];
void oula(){
	int m=0,n;
	cin>>n;
//	int cnt=0;
	for(int i=2;i<=n;i++){
		if(v[i]==0){
			v[i]=i,prime[++m]=i;
			phi[i]=i-1;
		}
		for(int j=1;j<=m;j++){
//			cnt++;
			if(prime[j]>v[i] || prime[j]*i>n) break;
			//只会执行一次 
			v[i*prime[j]]=prime[j];
			phi[i*prime[j]]=phi[i]*(i%prime[j]? prime[j]-1:prime[j]); 
		}
	}
//	cout<<cnt<<endl;
	rep(i,2,m){
		 cout<<"i:"<<i<<" prime:"<<prime[i]<<" "<<endl;
		 
	}
	rep(i,1,n){
		cout<<"i:"<<i<<" v:"<<v[i]<<" phi:"<<phi[i]<<endl;
	}
}
int main (){
//   freopen("in.txt","r",stdin);
//  freopen("out.txt","w",stdout);
//  int  T;
//   T=read();
//   while(T--)
  while(1)
  oula(); 
  return 0;
}
输出结果:

在这里插入图片描述

6.大区间素数求法:

解决的问题:[a,b]的素数
数据范围: a<b<1e12 ,b-a<1e6

思路:

先用埃式筛法求[2,sqrt(b)]内的素数。
然后用素数来筛[a,b]区间的素数即可。
时间复杂度O(sqrt(b)loglogb) + O((b-a)sqrt(b-a)).

代码如下:
ps:不开long long 不见祖宗,记得开哈

//求大区间素数个数 
int f[1000005];    //标记,线性筛
ll ans[1000005];  //存放[a,b]的素数 
int E_sievepro_ve(ll a,ll b){
  //先求出sqrt(b)以内的素数。
	ll k=E_sievepro(sqrt(b)); 
	//cout<<k<<endl;
	//for(int i=1;i<=k;i++)cout<<prime[i]<<endl; 
	for(int i=0;i<=b-a;i++) f[i]=0;
	int index=0;
	for(ll i=1;i<=k;i++){
		ll c=a/prime[i];
		for(ll j=max(c,(ll)2)*prime[i];j<=b;j+=prime[i]){
			//cout<<j<<" ";
			//对f[]处理一下,a太多了,数组就那么点大。
			if(j>=a)  f[j-a]=1;  /
		}
	}
	for(ll i=a;i<=b;i++){
	   if(!f[i-a]) ans[++index]=i;	
	}
	cout<<"ans:"<<endl;
	for(int i=1;i<=index;i++)
	  cout<<ans[i]<<" "; cout<<endl;
	  return index;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

axtices

谢谢您的打赏

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

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

打赏作者

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

抵扣说明:

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

余额充值