素数筛法
上一回,我们介绍了素数的判定法,这一次,我们主要来介绍一下素数筛法。所谓素数筛法,就是在一个很大范围的数中,筛选出素数
埃氏筛法
埃氏筛法是素数筛法中非常简单的一种筛法,但是由于其复杂度相对于另一种欧拉筛法来说比较高,我们用的不多
算法原理:
我们知道对于一个数而言,其倍数一定不是素数,埃氏筛法正是利用这一点,从小到大枚举每一个数字,将自己的倍数标记为合数,到最后,没有被标记的就是素数
时间复杂度分析:
埃氏筛法的时间复杂度分析很有意思,我们来讨论一下:
上面的分析事实上并不严谨且不是重点,如果数学知识有所欠缺的读者可以不去理会,我们重点看一下代码实现
代码实现:
int v[MAXN];
void prime(int n){
memset(v,0,sizeof v);
for(int i=2;i<=n;i++){
if(v[i])continue;
for(int j=i;j<=n/i;j++){
v[i*j]=1;
}
}
}
欧拉筛法
欧拉筛法又叫线性筛法,是我们最为常用的素数筛法,相比于埃氏筛法,它的时间复杂度要更低
算法原理:
埃氏筛法中,有些步骤是重复做了的,一个数x的因子有很多,我们使用埃氏筛法,遍历到每个因子的时候都会筛一遍x,这无疑增加了不必要的时间成本,欧拉筛法中,我们保证每个应该被筛的数只会被它的最小质因数筛掉一次,欧拉筛的时间复杂度是: O(n),我们在这里不做证明,读者可以自行分析并计算之。在欧拉筛中,我们每遍历到一个2到n之间的数,如果其没被筛掉,我们就把它存进素数数组,并且我们遍历素数数组中的所有素数,此时,由于单调性,所有i*primes[j]的数的最小质因数一定是primes[j],特殊情况就是i自己就满足其是primes[j]的倍数,我们直接break即可,大家结合下面的代码看
代码实现:
const int N=1e5+10;
int n,primes[N],cnt;
bool vis[N];
void euler(){
for(int i=2;i<=n;i++){
if(!vis[i]) primes[++cnt]=i;
for(int j=1;j<=cnt&&i*primes[j]<=n;j++){
vis[i*primes[j]]=1;
if(i%primes[j]==0){
break;
}
}
}
}
光看不练假把式,下面的练习题各位可以做一下!
练习题
质数距离
题目给定一个闭区间[L,R],你需要找出区间内距离最近的两个质数以及最远的两个质数
数据范围
1<=L<R<=2^31-1
输入样例
2 17
14 17
输出样例
2,3 are closest, 7,11 are most distant.
There are no adjacent primes.
--选自《算法竞赛进阶指南》或者ACwing 196题
AC Code:
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=1000010;
typedef long long ll;
int primes[N],cnt;
bool vis[N];
void euler(int n){
memset(vis, 0, sizeof vis);
cnt = 0;
for (int i = 2; i <= n; i ++ )
{
if (!vis[i]) primes[cnt ++ ] = i;
for (int j = 0; primes[j] * i <= n; j ++ )
{
vis[i * primes[j]] = true;
if (i % primes[j] == 0) break;
}
}
}
int main(){
int l,r;
while(cin>>l>>r){
euler(50000);
memset(vis,0,sizeof vis);
for(int i=0;i<cnt;i++){
ll p=primes[i];
for(ll j=max((l+p-1)/p*p,2*p);j<=r;j+=p){
vis[j-l]=1;
}
}
cnt=0;
for(int i=0;i<=r-l;i++){
if(!vis[i]&&i+l>=2){
primes[cnt++]=i+l;
}
}
if(cnt<2){
cout<<"There are no adjacent primes."<<endl;
}
else{
int minp=0,maxp=0;
for(int i=0;i+1<cnt;i++){
int d=primes[i+1]-primes[i];
if(d<primes[minp+1]-primes[minp]) minp=i;
if(d>primes[maxp+1]-primes[maxp]) maxp=i;
}
printf("%d,%d are closest, %d,%d are most distant.\n",primes[minp],primes[minp+1],primes[maxp],primes[maxp+1]);
}
}
return 0;
}
点评: 这题的核心思路是一个数x如果是合数,一定存在小于等于sqrt(x)的质因子,我们可以据此筛掉区间内质因子倍数的数,剩下的就是质数了,再进行计算即可