一、引入
说到质数,你想到了什么?我们很容易判断一个数是不是质数,我们只要从2到 n \sqrt{n} n遍历,如果有n的因数就返回false,否则返回true。本文不是介绍质数判断,而是介绍在一个区间内得到所有质数的方法——质数筛法1,这里着重介绍质数筛法当中的埃氏筛法和欧拉筛法。
二、最朴素也最慢的方法——逐个判断
简介
得到一个区间内所有的质数,最简单、朴素的方法应该就是遍历每个数,判断是否是质数了。这种方法的代码简单,但时间复杂度较高,适合于小数据量。就是文章开头注释中提到的方法。
程序
出于完整性的考虑,还是将这种方法的程序列出在下方了。给出的代码只是找到n以下的质数。注意,由于数组大小所限,这段代码的n必须 ≤ 1000 \leq1000 ≤1000。
bool isPrime(int n){
if(n<=1){
return false;
}
for(int i=2;i*i<=n;++i){
if(n%i==0){
return false;
}
}
return true;
}
bool Prime[1000]={};
void SM(int n){
for(int i=2;i<=n;++i){
Prime[i]=isPrime(i);
}
}
三、埃氏筛法
简介
埃氏筛法是从2开始,“划掉”所有非被标记的数的倍数,直到遍历结束。这样就得到了一个数组 a a a, a i a_i ai就是 i i i是否是质数的标记。
过程
从二开始,到n结束。如果
a
i
a_i
ai不是质数,就跳过,否则用循环将
i
i
i的倍数标记为非质数。这里其实有两种写法。片段如下:
写法一:
for(int j=2;i*j<=n;++j){
Prime[i*j]=1;
}
写法二:
for(int j=i*2;j<=n;j+=i){
Prime[j]=1;
}
程序2
void SieveE(int n){
for(int i=0;i<=n;++i) Prime[i]=0;
for(int i=2;i<=n;++i){
if(Prime[i]){
continue;
}
//这里使用了写法二,如果想使用写法一可自行替换
for(int j=i*2;j<=n;j+=i){
Prime[j]=1;
}
}
}
三、欧拉筛法
简介
虽然埃氏筛法很快,但它有一个问题,就是一个数可能被标记为非质数很多次,很浪费时间。欧拉筛法就解决了这个问题。它的时间复杂度是 O ( n ) O(n) O(n),而埃氏筛法的时间复杂度是 O ( n l o g l o g n ) O(n\,log\,log\,n) O(nloglogn)。可见欧拉筛法的时间复杂度更优。
过程
i
i
i从2开始到n结束,每次遍历:
如果
a
i
a_i
ai标记为是质数,那么将
i
i
i加入质数列表。
在所有情况下,都进行下面的运算:
如果
i
×
P
[
j
]
≤
n
i×P[j]≤n
i×P[j]≤n(未超过范围),则标记
a
[
i
×
P
[
j
]
]
a[i×P[j]]
a[i×P[j]] 为非质数;否则退出循环。
程序
void Sieve_Euler(int n){
for(int i=0;i<=n;++i) Prime[i]=0;
vector<int> P;
for(int i=2;i<=n;++i){
if(Prime[i]==0){
P.push_back(i);
}
for(int j=0;j<P.size();++j){
if(i*P[j]>n){
break;
}
Prime[i*P[j]]=1;
if(i%P[j]==0){
break; // 确保每个合数只被最小质因子标记一次
}
}
}
}
结束
筛选质数在编程中是很重要的一个操作,本文列举了三种常用的质数筛法3。掌握它们对编程是很有帮助的。
978

被折叠的 条评论
为什么被折叠?



