用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 兩位技術前輩,可以說我在這里只是把他們的心得進一步總結。權當筆記。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM