暴力判断素数、埃氏筛法、欧拉筛法

筛法是一种用来求素数的方法。一般来说可以循序渐进的掌握暴力判断素数、埃氏筛法、欧拉筛法。

暴力判断素数

时间复杂度 O(n²/2)(对于打出素数表来说)(我太蒻了,这个时间复杂度不保证正确)

利用素数只能被1和它本身整除的性质,我们可以把数x与2到n-1全取余。如果结果均不等于0,则为素数。

代码

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 
 5 using namespace std;
 6 
 7 int n;
 8 bool flag;
 9 
10 int main()
11 {
12     scanf("%d",&n);
13     
14     for(int i=2;i<=n;i++)
15     {
16         flag=true;
17         for(int j=2;j<=i-1;j++)
18         {
19             if(i%j==0)
20             {
21                 flag=false;
22                 //当出现第一个能被整除的数时立即跳出循环,可提高一定效率
23                 break;
24             }
25         }
26         
27         if(flag) printf("%d ",i);
28     }
29     
30     return 0;
31 }

这个最简单的写法在只判断几个素数时绰绰有余,但是当要打出一个很大的素数表时就很狼狈了。

 

埃氏筛法

时间复杂度 O(nloglogn)

很容易想到,任意一个素数的倍数是合数,这就是埃氏筛法筛出素数表的原理。假设要打出n以内的素数表。我们从2开始向后遍历,每当遇到一个素数p时,便利用此素数将[2,n]内p*2,p*3,p*4......这些合数全部筛去。

代码

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 #include <cmath>
 5 
 6 using namespace std;
 7 
 8 int n;
 9 int p[10000010];
10 
11 int main()
12 {
13     scanf("%d",&n);
14     
15     //初始化
16     for(int i=2;i<=n;i++) p[i]=1;
17     
18     for(int i=2;i<=sqrt(n)+0.5;i++)
19         for(int j=i*2;j<=n;j+=i) p[j]=0;
20     
21     for(int i=2;i<=n;i++)
22         if(p[i]) printf("%d ",i);
23     
24     return 0;
25 }

这个算法在一般的数据面前已经足够了。不过我们还可以更快:)

 

欧拉筛法

时间复杂度 O(n)

首先观察一下埃氏筛法。在使用他时,我们发现有这样一个问题:在使用素数2进行筛去操作时,我们会筛去素数6;在之后利用素数3进行筛去操作时,我们又会筛去一次素数6。可见在埃氏筛法的筛去过程中,会出现同一个合数筛去多次的情况,以至于影响了效率。

相比之下,欧拉筛法是一种线性筛法。它便解决了埃氏筛法中一个合数会被筛去多次的问题,确保了每一个合数都只会被筛去一次,因此具有O(n)的效率。而它的实现原理是确保每个合数都只会被它的最小质因数筛掉。过程大概如下:

首先我们会建立2个数组,isp(值为1/0,isp[i]用来表示i是否是素数),p(用来储存所有的素数)。依旧是打出到n的素数表。

1.将i从2开始遍历。如果i是素数,就把它加入到p[]中。

2.在当前i的情况下遍历p[],筛去i*p[j],若i*p[j]>n(已经没必要筛去了)或i%p[j]==0(这是确保每个合数只筛一次的核心)就退出循环。

对核心的解释:假设在i%p[j]==0时我们继续进行,便筛去了合数i*p[j+1]。由于此时i%p[j]==0,则这个由一个素数和一个合数的乘积组成的合数,一定能转化为一个更小的素数和一个更大的合数的乘积,那么当继续运行时,此合数必然还会被p[j]再次筛去,便进行了多余的筛去工作。因此可以说在i%p[j]==0时退出循环是确保每个合数都只会被它的最小质因数筛掉,也就是确保每个合数只被筛一次的核心。

代码

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 
 5 using namespace std;
 6 
 7 int n;
 8 int num=0;
 9 int isp[10000010],p[10000010];
10 
11 int main()
12 {
13     scanf("%d",&n);
14     
15     //初始化
16     for(int i=2;i<=n;i++) isp[i]=1;
17     
18     for(int i=2;i<=n;i++)
19     {
20         if(isp[i])
21         {
22             num++;
23             p[num]=i;
24         }
25         for(int j=1;j<=num&&i*p[j]<=n;j++)
26         {
27             isp[i*p[j]]=0;
28             //确保每个合数都只会被它的最小质因数筛掉
29             if(i%p[j]==0) break;
30         }
31     }
32     
33     for(int i=1;i<=num;i++) printf("%d ",p[i]);
34     
35     return 0;
36 }
posted @ 2020-10-25 10:05  CYHei_mu  阅读(276)  评论(0)    收藏  举报