质数筛法
什么是质数筛
质数筛是一种筛选质数的方法,比如我想要从1~10000中找出所有的质数,一一枚举肯定是不现实的,这个时候就需要一种快捷可行的方法来帮助我们。
几种质数筛
朴素筛法--时间复杂度O(n^(3/2))
质数就是不能被1和它本身整除的数,如果要判断x是否是质数,我们只需要遍历2~x-1,看其中有没有可以整除x的数。如果有,那么x就不是质数。 代码演示如下:
bool isPrime(int x){
if(n < 2){
return 0;
}
int tmp = sqrt(n);//sqrt()用于计算一个非负实数的平方根
for(int i = 2; i <= tmp; ++i){
if(x % i == 0){
return 0;
}
}
return 1;
}
埃氏筛法--时间复杂度O(nlog(logn))
埃氏筛法由埃拉托色尼提出,其基本原理是:一个合数总是可以分解成若干个质数的乘积,如果把质数的倍数都去掉,那么剩下的就是质数了。 代码演示如下:
#include <iostream>
#include <cmath>
using namespace std;
int p[10005], sum = 0;
bool isPrime[1000005];//isPrime[i] = 0 i是质数; isPrime[i] = 1 i是合数
int main(){
int n;
scanf("%d", &n);
isPrime[1] = 1;
for(int i = 2; i <= n; ++i){
if(isPrime[i] == 0){
sum++;
p[sum] = i;
for(int j = 2 * i; j <= n; j += i){//j就是i的倍数
isPrime[j] = 1;
}
}
}
for(int i = 1; i <= sum; ++i){
cout << p[i] << " ";
}
cout << endl;
return 0;
}
欧拉筛法--时间复杂度O(n)
在埃氏筛法中,对于同一个数可能会被筛选多次,比如6 = 2 * 3,那么6就会被2和3各筛选一次,造成了时间上的冗余。 欧拉筛法针对这个问题进行了改良:规定每个合数只会被它最小的质因数筛去,后面的质因数直接跳过,那么时间复杂度就减小到了O(n),因为对每个数字都只处理了一次,因此也被称作线性筛。 代码演示如下:
#include <iostream>
#include <cmath>
using namespace std;
int p[10005], sum = 0;
bool isPrime[1000005];//isPrime[i] = 0 i是质数; isPrime[i] = 1 i是合数
int main(){
int n;
scanf("%d", &n);
isPrime[1] = 1;
for(int i = 2; i <= n; ++i){//1.枚举2~n 2.枚举每个质数的倍数
if(isPrime[i] == 0){
sum++;
p[sum] = i;
}
//把i当作质数的倍数来看,枚举所有质数的i倍
for(int j = 1; j <= sum; ++j){
int x = i * p[j];//枚举质数表中所有元素的i倍
if(x > n){
break;
}
isPrime[x] = 1;
if(i % p[j] == 0){
break;//保证合数只被其最小的质因数标记筛去
}
}
}
for(int i = 1; i <= sum; ++i){
cout << p[i] << " ";
}
cout << endl;
return 0;
}
这里最关键的一步是
if(i % p[j] == 0)break;
因为如果没有这一步,那么我们在尝试筛去i * p[j + 1]时,会发现i可以分解为p[j] * i',那么最小的质因数就是p[j]而不是p[j + 1]了
对于欧拉筛,我们介绍一道模板题,巩固一下:
这道题的思路其实就是欧拉筛的标准过程,可以作为模板使用,下面是代码实现:
#include <iostream>
#include <vector>
using namespace std;
bool isPrime[100000005];//标记是否是质数
int p[6000005], sum = 0;//存储质数
int n, q, k;
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin >> n >> q;
isPrime[1] = 1;
for(int i = 2; i <= n; ++i){
if(isPrime[i] == 0){
sum++;
p[sum] = i;
}
for(int j = 1; j <= sum; ++j){
int tmp = p[j] * i;
if(tmp > n){
break;
}
isPrime[tmp] = 1;
if(i % p[j] == 0){
break;
}
}
}
int index;
vector<int> res;
for(int i = 1; i <= q; ++i){
cin >> index;
res.push_back(p[index]);
}
for(int i = 0; i < q; ++i){
cout << res[i] << endl;
}
}