求C++素数

  求素数算法
注意:下面的代码均是以C++编写的。
需求一:请实现一个函数,对于给定的整型参数n,该函数能够把自然数中,小于n的质数,从小到大打印出来。比如,当n=10,则打印出2357
方案1:试除法
分析思路:首先我们了解一下素数的定义,所谓的素数指如果有一个正整数p只有两个因子1和p,则p为素数。而这里的试除即根据素数的定义,比如要判断自然数x是否质数,就不断尝试小于x且大于1的自然数,只要有一个能整除,则x是合数;否则,x是质数。
源代码:
#include<iostream>usingnamespacestd;//定义一个判断素数函数boolisPrime(intn){
if(n<2)returnfalse;
for(inti=2;i<n;++i)
if(n%i==0)returnfalse;returntrue;}
intmain(){
intn;//输出不大于n的素数cout<<"请输入一个正整数:"<<endl;cin>>n;
//输出所有的不大于n的素数for(inti=2;i<=n;i++){
if(isPrime(i))
cout<<i<<endl;
}
return0;}
评价:该算法的时间复杂度O(n2),从时间效率来说,它是效率最差的算法,也是最平庸的算法思路。
方案1改进一:
 
分析思路:改进的角度1:我们知道除了2以外,所有的素数都是奇数,所以当寻找所有不大于n的素数时,不需要从2到n都要遍历。而是除了2以外,只需要遍历不大于n的所有奇数即可。
改进的角度2:当我们判断一个数是否为素数时,试除时只需要从2到n的根号即可判断是否为素数。而且除2以外,所有的质数都是奇数则也就肯定不会被2整除,这样试除的遍历又可以不用遍历偶数。
源代码:
#include<iostream>#include<cmath>usingnamespacestd;
//定义一个判断素数函数boolisPrime(intn){
if(n<2)returnfalse;if(n==2)returntrue;
for(inti=3;i<sqrt(n);i+=2)
if(n%i==0)returnfalse;returntrue;}
intmain(){
intn;//输出不大于n的素数cout<<"请输入一个正整数:"<<endl;cin>>n;
//输出所有的不大于n的素数cout<<"2"<<endl;
for(inti=3;i<=n;i+=2){
if(isPrime(i))
cout<<i<<endl;
}
return0;}
方案1改进二:
分析思路:一些更加聪明的程序员,会发现一个问题:尝试从3到√x的所有奇数,还是有些浪费。比如要判断101是否质数,101的根号取整后是10,那么,按照上述算法,需要尝试的奇数分别是:3,5,7,9。但是你发现没有,对9的尝试是多余的。不能被3整除,必然不能被9整除......顺着这个思路走下去,这些程序员就会发现:其实,只要尝试小于√x的质数即可。而这些质数,恰好前面已经算出来了(是不是觉得很妙?)。在具体的算法实现时,我们借助了一个单链表来存储上一次所有遍历出来的素数,顺便说一下,这就是算法理论中经常提到的:以空间换时间。
源代码:
  #include<iostream>#include<cmath>usingnamespacestd;classlist{public:
intdata;list*next;};
voidprime_number(intm,list*L);intmain(){
intm;list*L;L=NULL;
cout<<"请输入一个大于2的整数:";
cin>>m;
prime_number(m,L);return0;}
voidprime_number(intm,list*L){
intjudge=0;//用于判断是否为素数,是值为0,否则为1.if(m<2){
cout<<"输出的m值小了,没有质数!"<<endl;return;}
list*p,*s;p=L;
cout<<"2"<<endl;
for(inti=3;i<=m;i+=2){
p=L;
while(p!=NULL){
if(i%p->data==0){
judge=1;break;}
p=p->next;}
if(judge==0){
cout<<i<<endl;s=newlist();
s->data=i;s->next=L;L=s;
}
judge=0;}}
方案2:筛选法
分析思路:估计很多人把筛法仅仅看成是一种具体的方法。其实,筛法还是一种很普适的思想。在处理很多复杂问题的时候,都可以看到筛法的影子。那么,筛法如何求质数的,说起来很简单:
首先,2是公认最小的质数,所以,先把所有2的倍数去掉;然后剩下的那些大于2的数里面,最小的是3,所以3也是质数;然后把所有3的倍数都去掉,剩下的那些大于3的数里面,最小的是5,所以5也是质数......上述过程不断重复,就可以把某个范围内的合数全都除去(就像被筛子筛掉一样),剩下的就是质数了。
聪明的程序猿会构造一个定长的布尔型容器(通常用数组)。比方说,质数的分布范围是1,000,000,那么就构造一个包含1,000,000个布尔值的数组。然后把所有元素都初始化为true。在筛的过程中,一旦发现某个自然数是合数,就以该自然数为下标,把对应的布尔值改为false。全部筛完之后,遍历数组,找到那些值为true的元素,把他们的下标打印出来即可。
源代码:
#include<iostream>usingnamespacestd;
voidprime_number(intm);
intmain(){
intm;
cout<<"请输入一个大于2的整数:"<<endl;cin>>m;
prime_number(m);return0;}
voidprime_number(intm){
inti,j,k;bool*Num;
Num=newbool[m];for(i=0;i<m;i++){
if(i==0)Num[i]=false;elseNum[i]=true;}
k=1;
while(k<m){
for(i=k;i<m;i++){
if(Num[i]){
for(j=i+1;j<m;j++){
if((j+1)%(i+1)==0)Num[j]=false;}
break;}}
k=i+1;}
for(i=0;i<m;i++){
if(Num[i])cout<<i+1<<endl;}
delete[]Num;}
需求二:请实现一个函数,对于给定的整型参数N,该函数能够从小到大,依次打印出自然数中最小的N个质数。比如,当N=10,则打印出2357111317192329
方案1:
分析思路:方案一的思路很简单,使用while循环从最小素数2开始遍历,通过声明一个变量count统计遍历出素数个数,当count达到欲求的个数时停止while循环。在具体的实现时,可以根据上面需求一的情况对素数的判断与遍历做一些优化。
源代码:
#include<iostream>#include<cmath>usingnamespacestd;
boolIsPrime(int);voidAllPrime(int);intmain(){
intn;
//n表示要输出n个最小素数
cout<<"请输入一个正整数:"<<endl;cin>>n;AllPrime(n);return0;
}
//IsPrime函数判断一个数是否为素数boolIsPrime(intm){
if(m<2)returnfalse;if(m==2)returntrue;
for(inti=3;i<sqrt(m);i+=2){
if(n%i==0)returnfalse;}
returntrue;
}
//AllPrime函数输出n个最小素数voidAllPrime(intn){
if(n<1)cout<<"输入无效数字!"<<endl;if(n==1){
cout<<"2"<<endl;return;}
cout<<"2"<<endl;inti=3,count=1;while(count<n){
if(IsPrime(i)){
cout<<i<<endl;count++;}i+=2;}}
方案2:分析思路:
素数定理可以给出第n个素数p(n)的渐近估计:p(n)约等于nlogn.实际上p(n)一般不会大于(1+15%)*nlogn.所以方案二根据这个思路先确定一个p(n)(肯定包含n个素数),再用筛选法排除p(n)的所有合数,最后输出n个素数即可。
源代码:
#include<iostream>#include<cmath>usingnamespacestd;
voidprime_number(int,int);
intmain(){
intm,n;
cout<<"请输入一个大于2的整数:"<<endl;cin>>n;
m=n*log(n)*1.5;prime_number(m,n);return0;}
voidprime_number(intm,intn){
inti,j,k,count=0;bool*Num;
Num=newbool[m];for(i=0;i<m;i++){
if(i==0)Num[i]=false;elseNum[i]=true;}k=1;
while(k<m){
for(i=k;i<m;i++){
if(Num[i]){
for(j=i+1;j<m;j++){
if((j+1)%(i+1)==0)Num[j]=false;}break;}}
k=i+1;}
for(i=0;i<m;i++){
if(Num[i]){
cout<<i+1<<endl;count++;}
if(count==n)break;}
delete[]Num;}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值