C语⾔——判断⼀个数是否为质数素数
定义:约数只有1和本⾝的整数称为质数,或称素数。
计算机或者相关专业,基本上⼤⼀新⽣开始学编程都会接触的⼀个问题就是判断质数,下⾯分享⼏个判断⽅法,从普通到⾼效。
1)直观判断法
最直观的⽅法,根据定义,因为质数除了1和本⾝之外没有其他约数,所以判断n是否为质数,根据定义直接判断从2到n-1是否存在n的约数即可。C++代码如下:
bool isPrime_1( int num )
{
int tmp =num- 1;
for(int i= 2;i <=tmp; i++)
if(num %i== 0)
return 0 ;
return 1 ;
}
2)直观判断法改进
上述判断⽅法,明显存在效率极低的问题。对于每个数n,其实并不需要从2判断到n-1,我们知道,⼀个数若可以进⾏因数分解,那么分解时得到的两个数⼀定是⼀个⼩于等于sqrt(n),⼀个⼤于等于sqrt(n),据此,上述代码中并不需要遍历到n-1,遍历到sqrt(n)即可,因为若sqrt(n)左侧不到约数,那么右侧也⼀定不到约数。
C++代码如下:
1. bool isPrime_2( int num )
2. {
3. int tmp =sqrt( num);
4. for(int i= 2;i <=tmp; i++)c++判断素数
5. if(num %i== 0)
6. return 0 ;
7. return 1 ;
8. }
3)另⼀种⽅法
⽅法(2)应该是最常见的判断算法了,时间复杂度O(sqrt(n)),速度上⽐⽅法(1)的O(n)快得多。最近在⽹上偶然看到另⼀种更⾼效的⽅法,暂且称为⽅法(3)吧,由于不到原始的出处,这⾥就不贴出链接了,如果有原创者看到,烦请联系我,必定补上版权引⽤。下⾯讲⼀下这种更快速的判断⽅法;
⾸先看⼀个关于质数分布的规律:⼤于等于5的质数⼀定和6的倍数相邻。例如5和7,11和13,17和19等等;
证明:令x≥1,将⼤于等于5的⾃然数表⽰如下:······ 6x-1,6x,6x+1,6x+2,6x+3,6x+4,6x+5,6(x+1),6(x+1)+1 ······
可以看到,不在6的倍数两侧,即6x两侧的数为6x+2,6x+3,6x+4,由于2(3x+1),3(2x+1),2(3x+2),所以它们⼀定不是素数,再除去6x本⾝,显然,素数要出现只可能出现在6x的相邻两侧。这⾥有个题外话,关于孪⽣素数,有兴趣的道友可以再另⾏了解⼀下,由于与我们主题⽆关,暂且跳过。**这⾥要注意的⼀点是,在6的倍数相邻两侧并不是⼀定就是质数**。
此时判断质数可以6个为单元快进,即将⽅法(2)循环中i++步长加⼤为6,加快判断速度,原因是,假如要判定的数为n,则n必定是6x-1或6x+1的形式,对于循环中6i-1,6i,6i+1,6i+2,6i+3,6i+4,其中如果n能被6i,6i+2,6i+4整除,则n⾄少得是⼀个偶数,但是6x-1或6x+1的形式明显是⼀个奇数,故不成⽴;另外,如果n能被6i+3整除,则n⾄少能被3整除,但是6x能被3整除,故6x-1或6x+1(即n)不可能被3整除,故不成⽴。综上,循环中只需要考虑6i-1和6i+1的情况,即循环的步长可以定为6,每次判断循环变量k和k+2的情况即可,理论上讲整体速度应该会是⽅法(2)的3倍。代码如下: ``` bool isPrime_3( int num ) { //两个较⼩数另外处理 if(num ==2|| num==3 ) return 1 ; //不在6的倍数两侧的⼀定不是质数 if(num %6!= 1&&num %6!= 5) return 0 ; int tmp =sqrt( num); //在6的倍数两侧的也可能不是质数 for(int i= 5;i <=tmp; i+=6 ) if(num %i== 0||num %(i+ 2)==0 ) return 0 ; //排除所有,剩余的是质数 return 1 ; } ``` > 这边为什么是5开始取?为什么不是7?这边还有个逻辑上的取舍从5、7开始的区别在于i <= sqrt(num)..如果是5的话,判断条件为25;如果是7的话,判断的条件就为49。⽽仔细观察49内的所有质数,发现25之前的质数都是6k左右的数(6k-1,6k+1),⽽25以后,就不定都有了。如26则不为质数。
所以如果从5开始的话,那么25以内的数都不会进⼊for循环,经过`if(num %6!= 1&&num %6!= 5)`的筛选后,就都是素数了。⽽如果是从7开始,那么25-49之内的数不符合条件却不会进⼊for循环,所以26缺少这个for的循环判断后就被误判为素数了。 ==>以我浅薄的数学见识理解,25以内素数规律的巧合性使得这些数不需要进⼊for循环判断,所以相⽐于从7开始的错误,5开始是正确的
算法性能测试:
编写测试代码,使⽤较多数据测试⽐较⼏种⽅法的判断效率,数据量40w,代码如下:
#include <iostream>
#include <string>
#include <ctime>
#include <vector>
using namespace std;
bool isPrime_1( int num );
bool isPrime_2( int num );
bool isPrime_3( int num );
int main()
{
int test_num =400000;
int tstart ,tstop; //分别记录起始和结束时间
//测试第⼀个判断质数函数
tstart=clock ();
for(int i= 1;i <=test_num; i++)
isPrime_1(i );
tstop=clock ();
cout<<"⽅法(1)时间(ms):" <<tstop- tstart<<endl ;//ms为单位
//测试第⼆个判断质数函数
tstart=clock ();
for(int i= 1;i <=test_num; i++)
isPrime_2(i );
tstop=clock ();
cout<<"⽅法(2)时间(ms):" <<tstop- tstart<<endl ;
//测试第三个判断质数函数
tstart=clock ();
for(int i= 1;i <=test_num; i++)
isPrime_3(i );
tstop=clock ();
cout<<"⽅法(3)时间(ms):" <<tstop- tstart<<endl ;
cout<<endl ;
system("pause" );
return 0 ;
}
运⾏结果如下;
可以看出,判断到40w,效率上⽅法(1)明显要差得多,⽅法(2)和⽅法(3)在这种测试数量下时间相差2倍多
单独对⽐⽅法(2)和(3),数据量加到1000w,结果如下:
可以看出,⽅法(2)和⽅法(3)在这种测试数量下时间相差依然是2倍多,不过已经是很不错的提升。
对了,附上运⾏环境,CPU-i5-3210,内存4G,win7,vs2012。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论