文章目录
素数筛
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;
}