素数筛法
素数的筛法有很多种,但是基础就是对素数的判定。即,我们需要知道什么是素数,以及素数的一些性质,那么我们先讲一讲素数的性质(这一部分一定要好好掌握,对考试有很大的帮助):
定义:只有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):素数有无穷多个
下面来证明这个结论:
用反证法,假设自然数中只有有限多个素数,可以记为P1 ,P2 ,P3 ……Pk ,这时存在一个正整数N = P1P2P3 ……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 * 2r 则要么ad ≡ 1(mod n),要么存在0 < i < r ,s.t.ad*2i ≡ -1 ( mod n )
知道了原理,就可以带进几个数进行计算,得出是否为素数(代码中的gg数组中的几个质数在int范围内不会判断出错),至于代码,也比较好理解,就不再说了。
这就是本篇博文的所有内容,请大佬们多多指正,不喜勿喷QwQ
2019-04-09 21:31:23