用BigInteger实现大素数生成算法

一.通过素数的基本性质

  根据素数的性质(除了1和此整数(n)自身外,无法被其他自然数整除的数):即从2到n/2的数都不能整除n。

 1 public static boolean isPrime(BigInteger num) 
 2 { 
 3     BigInteger two = BigInteger.valueOf(2); 
 4     for(BigInteger i = two; !(i.compareTo(num.divide(two)) == 1); i = i.add(BigInteger.ONE)) 
 5     { 
 6         if(num.remainder(i) == BigInteger.ZERO) 
 7         { 
 8             return false; 
 9          } 
10     } 
11                  
12     return true; 
13 }     

  用大于2^63的数去测试,结果因为运算量太大,运行半个来小时也没有结果出现。

二.通过素数表

  要提高速度就要减少进入判断方法中的循环:

  1.偶数可以排除

  2.大的合数(即素数的积)可以排除

  排除偶数直接增加一个判断即可实现,而排除大的合数也通过产生一个素数表实现。

  这里引援51CTO网友 梦朝思夕 BOLG,即“一般来说整除100 以内的所有素数可排除76% 不是素数的可能性 整除256以内的所有素数可排除80% 不是素数的可能性。” 而我同样地建大小为2000的表,private static BigInteger[] primeList = new BigInteger[2000]

primeList[1999] = 17389

for(int i = 0, j = 2; i < 2000; j++)
{
    if(isPrime(j))
    {
        primeList[i] = BigInteger.valueOf(j);
        i++;
    }
}

  再来一个方法判断新生成的大数判断是否为几个素数的积

public static boolean isNotPrimeProduct(BigInteger num)
{
    for(int i=0;i< 2000; i++)
    {
          if(num.remainder(primeList[i]) == BigInteger.ZERO)
          {
                return false;
           }
         }
        
       return true;
}

素数表太大也减慢速度,而且数值越大,素数表判别的确定性就越小。要知道,我们要的是2^63。

 

三.通过费马(Fermat)素数检验

  在网上查阅资料,知道可以运用费马小定理检验一个数是否不是合数。

 费马小定理是数论中的一个定理:假如a是一个整数,p是一个质数,那么
a^p \equiv a \pmod{p}

如果a不是p的倍数,这个定理也可以写成

a^{p-1} \equiv  1 \pmod{p}

                                                                    来自wiki

 根据费马小定理:如果p是素数,1 \le a \le p,那么
a^{p-1} \equiv 1 \pmod{p}

如果我们想知道n是否是素数,我们在中间选取a,看看上面等式是否成立。如果对于数值a等式不成立,那么n是合数。如果有很多的a能够使等式成立,那么我们可以说n 可能是素数,或者伪素数。

在我们检验过程中,有可能我们选取的a都能让等式成立,然而n却是合数。这时等式

a^{n-1} \equiv 1 \pmod{n}

被称为Fermat liar。如果我们选取满足下面等式的a

a^{n-1} \not\equiv 1 \pmod{n}

那么a也就是对于n的合数判定的Fermat witness

                                                                   来自wiki

  而在这里我从cnblogs的Knuth_档案学到了大量理论知识和算法的实现。(特别是蒙哥马利快速积模算法:计算大数(x^y)%z)

  用java实现如下

public static BigInteger Montgomery(BigInteger n, BigInteger p, BigInteger m)
    {
        n = n.remainder(m) ;
        BigInteger k = BigInteger.ONE;
        while(p.compareTo(BigInteger.ONE) == 0)
        {
            if(!(p.remainder(BigInteger.ONE) == BigInteger.ZERO))
            {
                k = (k.multiply(n)).remainder(m);
            }
            n = (n.multiply(n)).remainder(m);
            p = p.divide(BigInteger.valueOf(2));
        }
        
        return (n.multiply(k)).remainder(m);
    }

 

  接下来,我们就可以对一个大数使用费马素数检验可以判定这个大数是伪素数。

  从前2000素数一一检验,而费马素数检验只是随机化了。

public static boolean fermatPrimalityTest(BigInteger num)
    {
        for(int i = 0; i < 2000; i++)
        {
            if(!(Montgomery(primeList[i], num.subtract(BigInteger.ONE), num).compareTo(BigInteger.ONE) == 1))  //(x^y)%z
            {
                return false;
            }
        }
        
        return true;
    }

 

 

使用素数表的前十个结果:

9223372036854775809
9223372036854775811
9223372036854775813
9223372036854775815
9223372036854775817
9223372036854775819
9223372036854775821
9223372036854775823
9223372036854775825
9223372036854775827

使用费马素数检验过的前十个结果:

9223372036854775817
9223372036854775837
9223372036854775889
9223372036854775903
9223372036854775907
9223372036854775931
9223372036854775937
9223372036854775939
9223372036854775949
9223372036854775963

四.总结

  现在我们可以通过结果简单的分析出出只是使用素数表的结果有很多都通不过费马素数检验,因为素数表总有上界。最后可以通过Knuth所说的 拉宾米勒测试排除掉那些卡尔麦克(Carmichael)数。

 

  最后再次感谢 梦朝思夕Knuth 两位技术前辈,可以说我在这里只是把他们的心得进一步总结。权当笔记。

posted @ 2012-11-24 23:15  Edward@CS  阅读(7663)  评论(1编辑  收藏  举报