质数(prime number)又称素数,有无限个。一个大于1的自然数,除了1和它本身外,不能被其他自然数(质数)整除,换句话说就是该数除了1和它本身以外不再有其他的因数;否则称为合数。
——百度百科
要想判断一个数是不是质数,就要看他能不能被除了1和自己之外的数分解
显然如果当需要多次判断数据是否是素数时,每次都计算一次是很慢的。
但是可以声明一个布尔型数组,保存对应下标是否为素数,这样只需要计算一次。
开始时,从2开始,分别乘上2以上的数,所得的数全都不是素数
此部分证明有误
1 #include <iostream>
2 #include <string>
3 #include <cstring>
4 using namespace std;
5 bool is_prime[10001];
6 int main(){
7 memset(is_prime,true,sizeof(is_prime));
8 for(int i=2;i<=100;i++){
9 for(int j=2;j<=100;j++){
10 is_prime[i*j]=false;
11 }
12 }
13 // printf("%d",is_prime[4]);
14 int num;
15 //cin>>num;
16 for (num=2;num<100;num++){
17 if(is_prime[num]==true){
18 cout<<num<<"是素数"<<endl;
19 }else{
20 cout<<num<<"不是素数"<<endl;
21 }
22 }
23 return 0;
24 }
显然,对于12,这样既计算了2*6,也计算了6*2(还有3*4,4*3),所以这样重复计算了大量数据
修改一下算法,可以让运算量稍小。
合数可以分解成质数相乘,而根据算法,如果一个合数已经被确定,那么由它得出的合数也都被确定。
所以,如果发现一个数是合数,那么就不必再计算由它乘出的数
同时,保证相乘的两个数都是前一个小后一个大,这样可以避免同一组数的重复计算。
Eratosthenes筛法
1 #include <iostream> 2 #include <cstring> 3 #include <cmath> 4 using namespace std; 5 const int maxn=10000; 6 bool is_prime[maxn+1]; 7 int main(){ 8 int m=sqrt(maxn+0.5); 9 memset(is_prime,true,sizeof(is_prime)); 10 for(int i=2;i<=m;i++){ 11 if(is_prime){ 12 for(int j=i*i;j<=maxn;j+=i){ 13 is_prime[j]=false; 14 } 15 } 16 } 17 18 for(int num=2;num<100;num++){ 19 if(is_prime[num]==true){ 20 cout<<num<<"是素数"<<endl; 21 }else{ 22 cout<<num<<"不是素数"<<endl; 23 } 24 } 25 return 0; 26 }
然而,如果我们添加一个count变量来记录每个数被重复运算的次数。会发现,仍然有好多数被重复运算。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <iostream> 2 #include <cstring> 3 #include <cmath> 4 using namespace std; 5 const int maxn=10000; 6 bool is_prime[maxn+1]; 7 int main(){ 8 int count[maxn+1]; 9 memset(count,0,sizeof(count)); 10 11 int m=sqrt(maxn+0.5); 12 memset(is_prime,true,sizeof(is_prime)); 13 for(int i=2;i<=m;i++){ 14 if(is_prime){ 15 for(int j=i*i;j<=maxn;j+=i){ 16 is_prime[j]=false; 17 count[j]+=1; 18 } 19 } 20 } 21 22 for(int num=1;num<100;num++){ 23 if(is_prime[num]==true){ 24 cout<<num<<"是素数"<<endl; 25 }else{ 26 cout<<num<<"不是素数"<<endl; 27 } 28 } 29 30 for(int i=1;i<=maxn;i++)if(count[i]>1)cout<<i<<":"<<count[i]<<endl; 31 return 0; 32 }
但是是存在线性的筛法的
算术基本定理可表述为:任何一个大于1的自然数 N,如果N不为质数,那么N可以唯一分解成有限个质数的乘积N=P1a1P2a2P3a3......Pnan,这里P1<P2<P3......<Pn均为质数,其中指数ai是正整数。这样的分解称为 N 的标准分解式。最早证明是由欧几里得给出的,现代是由陈述证明。此定理可推广至更一般的交换代数和代数数论。
——百度百科
只需要计算出所有 i×(小于i的素数) ,那么我们便可以得到所有的合数。同时,通过排除掉i是(小于i的素数)的整倍数的情况来保证只计算一次
1 #include <iostream> 2 #include <cstring> 3 #include <cmath> 4 using namespace std; 5 const int maxn=10000; 6 int main(){ 7 int count[maxn+1]={0}; 8 9 int prime[maxn]={0},num_prime=0; 10 bool isNotPrime[maxn]={1,1}; 11 12 for(long i=2;i<maxn;i++){ 13 if(!isNotPrime[i])prime[num_prime++]=i; 14 for(int j=0;j<num_prime&&i*prime[j]<maxn;j++){ 15 isNotPrime[i*prime[j]]=true; 16 count[i*prime[j]]++; 17 if(!(i%prime[j]))break; 18 } 19 } 20 21 for(int num=1;num<100;num++){ 22 if(isNotPrime[num]!=true){ 23 cout<<num<<"是素数"<<endl; 24 }else{ 25 cout<<num<<"不是素数"<<endl; 26 } 27 } 28 29 for(int i=1;i<=maxn;i++)if(count[i]>1)cout<<i<<":"<<count[i]<<endl; 30 return 0; 31 }
运行后,可以发现没有每个数运行次数都是1