判断素数的算法

来源:互联网 发布:网络分销渠道 编辑:程序博客网 时间:2024/03/29 22:10

测试素数的方式很多,本文将介绍一些算法,能尽快的判断一个给定的数字是否为素数。

 

判断给定数字n是否是素数,一个比较简单的方法就是:式除。试着用2,3,... 直到根号n去除n。显然,如果n是素数,则没有一个数能整除n

一个比较好的改进是,只用2到根号n的素数做为除数,这样可以减少不少除法的次数,算法的代码可以表示为:

bool checkPrime (int n)

{

       intmax = static_cast<int>(sqrt(n));

       inti=2;

       while(i<=max)

       {

              if (n%i == 0)

              {

                     returnfalse;

              }

              i++;

       }

       returntrue;

}

但是这个算法的特点是,它不仅能够判断n是否为素食,同时,如果n为合数,该算法还能给出这个合数的一个因子。但是这个算法的效率不高。我们有什么办法,在不找出因子的情况下,能够判断n是否为素数。

以下这个公式是一个几乎可行的判断方法:

an-1= 1(mod n)

其中,a表示基数,是一个大于1的自然数。如果这个等式不满足,则可以肯定,n是个合数。如果这个等式满足,则n是一个素数,或者是伪素数。由此可见,这个算法存在一定的误差。我们先用几个数字试试看:

假设我取基数a等于2。等式可以写成:

2n-1= 1(mod n)

3带入,左边等于44 mod 3 1,正好等于右边,所以3为素数。通过尝试,发现571113都满足这个等式。看上取不错,因为这个计算过程要比之前提到的式除法简单多了,但是慢着,试试3415616451105,你发现这个式子突然不灵了。3415616451105这几个数字,虽然满足等式,但是都式合数,这四个数字就是基于2的伪素数。所以这个算法存在一定的误差。

很遗憾,即使选择其它基数(比如35),仍旧存在一些伪素数不能被检查出来。这些数字被称为Carmichael。其实Carmichael数很少,在小于100 000 000的数字中,Carmichael数只有255个。这个算法的代码如下:

bool checkPrime(int n,int a)

{

       intt = pow(a,n-1);

       if(t%(n)==1)

       {

              returntrue;

       }

       else

       {

              returnfalse;

       }

       returnfalse;

}

 

下面将介绍MillerRabin算法,来减少出错的概率。

MillerRabin算法的改进主要有一下两点:

1)使用随机选取多个基数,而不是只选取2

2)使用辅助过程,来判断是否为合数

 

witness的算法大致如下:

首先,将n-1写成2tu的形式。其中,u必须是奇数。比如:

n561  n-1 = 560 = 24*35

然后计算X0 a u mod n

循环,i1t,计算Xi X2i1 mod n

如果Xi等于1,而且Xi1不等于1而且Xi1不等于n1,则返回true,表示找到合数

当循环结束,Xt不等于1,则返回true,表示合数

否则,返回false,表示该数为素数。

 

 

现在大致介绍一下代码:

bool checkPrime(int n,int s)

{

       intj;

       for(j=1;j<s;j++)

       {

              inta=rand();

              if(witness(a,n))

              {

                     returnfalse;

              }

       }

       returntrue;

}

 

代码的结构很简单,参数s表示循环的次数,每次循环,都会随机得到一个值作为基数。然后调用witness验证是否为合数。

虽然MillerRabin的结果仍旧有可能产生误差,但是这个误差是可以接受的。该误差的存在概率很小,且这个误差的可能性并不依赖于n,而是取决于s的大小和随机数a的运气。总之,错误发生的概率很小。