素数求法

本篇博客介绍三种素数的求法,即朴素求法、埃氏筛法和欧拉筛(线性筛)。

1)朴素求法

朴素求法的思想是:若 n 能整除 1 和 n 之外的数,则说明 n 是合数,否则 n 是素数。

 1 bool judge(int n){
 2     if(n==1) return false;
 3     else{
 4         for(int i=2;i*i<=n;i++){
 5             if(n%i==0){
 6                 return false;
 7             }
 8         }
 9         return true;
10     }
11 }

(2)埃氏筛法

埃式筛法的思想是:对于不超过 n 的每个非负整数 p,删除 2*p, 3*p, 4*p,...,当处理完所有数之后,还没有被删除的就是素数。如果用 vis[i] 表示 i 已经被删除,筛法的代码可以写成:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=10000000;
 4 bool vis[maxn+5];    //0代表素数,1代表非素数
 5 void Init(int n){
 6     memset(vis,0,sizeof(vis));
 7     vis[1]=1;      //1既不是素数也不是合数
 8     for(int i=2;i<=n;i++){
 9         for(int j=i*2;j<=n;j+=i){
10             vis[j]=1;
11         }
12     }
13 }
14 int main(){
15     Init(maxn);
16     return 0;
17 }

下面来改进这份代码。首先,在 “ 对于不超过 n 的每个非负整数 p ” 中,p 可以限定为素数——只需在第二重循环前加一个判断  if(!vis[i]) 即可。另外内层循环也不必从 i*2 开始,因为它已经在 i=2 时被筛掉了。然后,将外层循环的下界改为 m ,因为大于 m 的素数的倍数已经被筛过了。改进后的代码如下:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=10000000;
 4 bool vis[maxn+5];    //0代表素数,1代表非素数
 5 void Init(int n){
 6     int m=sqrt(n+0.5);    
 7     memset(vis,0,sizeof(vis));
 8     vis[1]=1;      //1既不是素数也不是合数
 9     for(int i=2;i<=m;i++){
10         if(!vis[i]){
11             for(int j=i*i;j<=n;j+=i){
12                 vis[j]=1;
13             }
14         }
15     }
16 }
17 int main(){
18     Init(maxn);
19     return 0;
20 }

(上述埃氏筛法的内容摘自算法竞赛宝典入门经典(第二版))

(3)欧拉筛(线性筛)

欧拉筛的思想是:在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达到不重复的目的。

代码如下:

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int maxn=10000000;
 4 bool vis[maxn+5];    //0代表素数,1代表非素数
 5 int prime[maxn+5];   //prime[i]表示第i个素数
 6 void Init(int n){
 7     int c=0;   //素数计数
 8     memset(vis,0,sizeof(vis));
 9     vis[1]=1;      //1既不是素数也不是合数
10     for(int i=2;i<=n;i++){
11         if(!vis[i]){      //将素数记录在prime数组
12             prime[++c]=i;
13         }
14         for(int j=1;j<=c&&i*prime[j]<=n;j++){   
15             vis[i*prime[j]]=1;
16             if(i%prime[j]==0){  //保证每个合数只会被它的最小质因数筛去
17                 break;
18             }
19         }
20     }
21 }
22 int main(){
23     Init(maxn);
24     return 0;
25 }

其中 prime[] 数组中的素数是递增的,当i能整除 prime[j],那么 i*prime[j+1] 这个合数肯定可以被 prime[j] 乘以某个数筛掉。因为 i=k*prime[j],那么 i*prime[j+1]=k*prime[j]*prime[j+1]=(k*prime[j+1])*prime[j]=k'*prime[j],接下来的素数同理,所以不用筛下去了。

猜你喜欢

转载自www.cnblogs.com/chxnote/p/10828986.html