素数筛法

  素数的筛法有很多种,但是基础就是对素数的判定。即,我们需要知道什么是素数,以及素数的一些性质,那么我们先讲一讲素数的性质(这一部分一定要好好掌握,对考试有很大的帮助):

  定义:只有1和自身作为因子(就是因数,不用我再赘述了)的数叫做素数(也叫质数)。

  性质(1):以π(x)表示不超过x的素数个数,可以证明----lim π(x) ln x ÷ x = 1(lim表示x趋近于正无穷)

    根据这一性质,我们可以推得一个式子,它长这样:

         lim π(x) =  x / ln x,

    虽然我暂时还没有用到这个式子,但是听钟神说可以利用这个式子估计算法的复杂度,就先记下来了。

  性质(2):设a是任意大于1的整数,则a的除1以外的最小正约数q必是素数;当a是合数时,q ≤ sqrt( a )

    我们来证明一下:

    我们可以用反证法,假设q不是素数,由q不为一可得q一定是合数,此时,存在q1为q的因子,且满足1 < q1 < q,q1 | q;又因为q |a,这与q是a的除1以外的最小正约数矛盾;

    当a是合数时,设a = a1q,其中q为a的除1以外的最小正约数,则a1 ≥ q,q2 ≤ a,即q ≤ sqrt ( a )  

  性质(3):素数有无穷多个

    下面来证明这个结论:

    用反证法,假设自然数中只有有限多个素数,可以记为P,P,P……Pk ,这时存在一个正整数N = P1P2P……Pk +1,这时由性质(2)可得一定有一个素数P,使得P | N,但是P一定不是已知的素数(否则P | 1),我们就找到了另一个素数,与假设不相符,得证

  

  讲完了这几个性质,我们便可以进行对素数的判别了(这一部分我将结合代码实现讲解)

  筛法一:暴力筛

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstdlib>
 4 #define maxn 10000010
 5 using namespace std;
 6 bool zi_ran_shu[maxn];
 7 bool shai_su_shu(int); //如果这个数是质数,就返回1,否则,就返回0;
 8 int main()
 9 {
10     int m;
11     scanf("%d",&m);
12     for(int i=1;i<=m;++i)
13         if(shai_su_shu(i)) zi_ran_shu[i]=1;//对每一个小于m的数进行判定,把判定结果存到zi_ran_shu数组里;
14     for(int i=1;i<=m;++i)
15         if(zi_ran_shu[i]) printf("%d ",i);
16     return 0;
17 }
18 
19 bool shai_su_shu(int a)
20 {
21     if(a==1) return 0;
22     if(a==2) return 1; //对1和2的特判
23     for(int i=2;i<=a-1;++i)//要从i=2开始模拟对a做除法,一直枚举到a-1;
24         if(a%i==0) return 0;//如果出现可以整除a的数,就说明a不是一个素数,return 0;
25     return 1;//如果没有找到一个数可以整除a,就说明a是一个素数,return 1;
26 }

  接下来,思考对这个算法的优化;

  如果没有思路,看看这句话 = = > 结合性质(2),可以想到将筛_素_数函数中的循环改成循环到sqrt(a),这样就完成了优化,这其实就是诶氏筛法

  筛法二:线性筛

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstdlib>
 4 #include<algorithm>
 5 #include<cmath>
 6 #include<iomanip>
 7 #include<cstring>
 8 #include<string>
 9 #define maxn 10000010
10 using namespace std;
11 int prime[maxn],tot=0;
12 bool is_prime[maxn];
13 int main()
14 {
15     memset(is_prime,true,sizeof(is_prime));
16     int n;
17     scanf("%d",&n);
18     for(int i=2;i<=n;++i)
19     {
20         if(is_prime[i]==true) prime[tot++]=i;
21         for(int j=0;j<tot && i*prime[j]<=n;++j)
22         {
23             is_prime[i*prime[j]]=false;//对所有质数的倍数进行排除(线性筛主要是减少了这一步的重复)
24             if(i%prime[j]==0) break;
25         }
26     }
27     for(int i=0;i<=n;++i)
28     {
29         if(prime[i]) printf("%d ",prime[i]);
30     }
31     return 0; 

   筛法三:Miller-Rabbin素性测试

 

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<cstdlib>
 4 using namespace std;
 5 int gg[8] = {2,3,5,7,13,29,37,89};
 6 
 7 int kuaisumi(int a,int b,int c){
 8     int ans = 1;
 9     int base = a%c;
10     while(b){
11         if(b & 1) ans = (ans*base)%c;
12         base = (base*base)%c;
13         b >>= 1;
14     }
15     return ans;
16 }
17 
18 bool miller_rabin(int a,int n)
19 {
20     int d=n-1,r=0;
21     while (d%2==0)
22         d/=2,r++;
23     int x = kuaisumi(a,d,n);
24     if (x==1) return true;
25     for (int i=0;i<r;i++)
26     {
27         if (x==n-1) return true;
28         x=(long long)x*x%n;
29     }
30     return false;
31 }
32 
33 bool is_prime(int n)
34 {
35     if (n<=1) return false;
36     for (int a=0;a<8;a++)
37         if (n==gg[a]) return true;
38     for (int a=0;a<8;a++)
39         if (!miller_rabin(gg[a],n)) return false;
40     return true;
41 }
42 
43 int main()
44 {
45     int n;
46     scanf("%d",&n);
47     for(int i=1;i<=n;++i)
48         if(is_prime(i)) printf("%d ",i);
49     return 0;
50 }

   先来介绍原理:如果n为素数,取a < n,设n - 1 = d * 2则要么ad ≡ 1(mod n),要么存在0 < i < r ,s.t.ad*2i ≡ -1 ( mod n )

  知道了原理,就可以带进几个数进行计算,得出是否为素数(代码中的gg数组中的几个质数在int范围内不会判断出错),至于代码,也比较好理解,就不再说了。

 

这就是本篇博文的所有内容,请大佬们多多指正,不喜勿喷QwQ

2019-04-09 21:31:23

posted @ 2019-04-09 21:32  蒟蒻hqk  阅读(1370)  评论(0编辑  收藏  举报