Miller-Rabin素?cái)?shù)測試

轉(zhuǎn)載自Matrix大牛
  一個(gè)數(shù)是素?cái)?shù)(也叫質(zhì)數(shù)),當(dāng)且僅當(dāng)它的約數(shù)只有兩個(gè)——1和它本身。規(guī)定這兩個(gè)約數(shù)不能相同,因此1不是素?cái)?shù)。對素?cái)?shù)的研究屬于數(shù)論范疇,你可以 看到許多數(shù)學(xué)家沒事就想出一些符合某種性質(zhì)的素?cái)?shù)并稱它為某某某素?cái)?shù)。整個(gè)數(shù)論幾乎就圍繞著整除和素?cái)?shù)之類的詞轉(zhuǎn)過去轉(zhuǎn)過來。對于寫代碼的人來說,素?cái)?shù)比 想像中的更重要,Google一下BigPrime或者big_prime你總會發(fā)現(xiàn)大堆大堆用到了素?cái)?shù)常量的程序代碼。平時(shí)沒事時(shí)可以記一些素?cái)?shù)下來以 備急用。我會選一些好記的素?cái)?shù),比如4567, 124567, 3214567, 23456789, 55566677, 1234567894987654321, 11111111111111111111111 (23個(gè)1)。我的手機(jī)號前10位是個(gè)素?cái)?shù)。我的網(wǎng)站域名的ASCII碼連起來(77 97 116 114 105 120 54 55 46 99 111 109)也是個(gè)素?cái)?shù)。
  素?cái)?shù)有很多神奇的性質(zhì)。我寫5個(gè)在下面供大家欣賞。
1. 素?cái)?shù)的個(gè)數(shù)無限多(不存在最大的素?cái)?shù))
  證明:反證法,假設(shè)存在最大的素?cái)?shù)P,那么我們可以構(gòu)造一個(gè)新的數(shù)2 * 3 * 5 * 7 * … * P + 1(所有的素?cái)?shù)乘起來加1)。顯然這個(gè)數(shù)不能被任一素?cái)?shù)整除(所有素?cái)?shù)除它都余1),這說明我們找到了一個(gè)更大的素?cái)?shù)。
2. 存在任意長的一段連續(xù)數(shù),其中的所有數(shù)都是合數(shù)(相鄰素?cái)?shù)之間的間隔任意大)
  證明:當(dāng)0<a<=n時(shí),n!+a能被a整除。長度為n-1的數(shù)列n!+2, n!+3, n!+4, …, n!+n中,所有的數(shù)都是合數(shù)。這個(gè)結(jié)論對所有大于1的整數(shù)n都成立,而n可以取到任意大。
3. 所有大于2的素?cái)?shù)都可以唯一地表示成兩個(gè)平方數(shù)之差。
   證明:大于2的素?cái)?shù)都是奇數(shù)。假設(shè)這個(gè) 數(shù)是2n+1。由于(n+1)^ 2=n^2 + 2n+1,(n+1)^ 2和n^2就是我們要找的兩個(gè)平方數(shù)。下面證明這個(gè)方案是唯一的。如果素?cái)?shù)p能表示成 a2-b2,則p=a^2- b^2=(a+b)(a-b)。由于p是素?cái)?shù),那么只可能a+b=p且a-b=1,這給出了a和b的唯一解。
4. 當(dāng)n為大于2的整數(shù)時(shí),2 ^n +1和2^n-1兩個(gè)數(shù)中,如果其中一個(gè)數(shù)是素?cái)?shù),那么另一個(gè)數(shù)一定是合數(shù)。
   證明:2^ n不能被3整除。如果它被3除余1,那么2^n -1就能被3整除;如果被3除余2,那么2^n +1就能被3整除??傊?^n +1和2^n -1中至少有一個(gè)是合數(shù)。
5. 如果p是素?cái)?shù),a是小于p的正整數(shù),那么a^(p-1) mod p = 1。
  這個(gè)證明就有點(diǎn)麻煩了。
  首先我們證明這樣一個(gè)結(jié)論:如果p是一個(gè)素?cái)?shù)的話,那么對任意一個(gè)小于p的正整數(shù)a,a, 2a, 3a, …, (p-1)a除以p的余數(shù)正好是一個(gè)1到p-1的排列。例如,5是素?cái)?shù),3, 6, 9, 12除以5的余數(shù)分別為3, 1, 4, 2,正好就是1到4這四個(gè)數(shù)。
  反證法,假如結(jié)論不成立的話,那么就是說有兩個(gè)小于p的正整數(shù)m和n使得na和ma除以p的余數(shù)相同。不妨假設(shè)n>m,則p可以整除a(n-m)。但p是素?cái)?shù),那么a和n-m中至少有一個(gè)含有因子p。這顯然是不可能的,因?yàn)閍和n-m都比p小。
   用同余式表述,我們證明了:
  (p-1)! ≡ a * 2a * 3a * … * (p-1)a (mod p)
   也即:
  (p-1)! ≡ (p-1)! * a^(p-1) (mod p)
  兩邊同時(shí)除以(p-1)!,就得到了我們的最終結(jié)論:
  1 ≡ a^(p-1) (mod p)
 可惜最后這個(gè)定理最初不是我證明的。這是大數(shù)學(xué)家Fermat證明的,叫做Fermat小定理(Fermat's Little Theorem)。Euler對這個(gè)定理進(jìn)行了推廣,叫做Euler定理。Euler一生的定理太多了,為了和其它的“Euler定理”區(qū)別開來,有些地 方叫做Fermat小定理的Euler推廣。Euler定理中需要用一個(gè)函數(shù)f(m),它表示小于m的正整數(shù)中有多少個(gè)數(shù)和m互素(兩個(gè)數(shù)只有公約數(shù)1稱 為互素)。為了方便,我們通常用記號φ(m)來表示這個(gè)函數(shù)(稱作Euler函數(shù))。Euler指出,如果a和m互素,那么a^φ(m) ≡ 1 (mod m)??梢钥吹?,當(dāng)m為素?cái)?shù)時(shí),φ(m)就等于m-1(所有小于m的正整數(shù)都與m互素),因此它是Fermat小定理的推廣。定理的證明和Fermat小 定理幾乎相同,只是要考慮的式子變成了所有與m互素的數(shù)的乘積:m_1 * m_2 … m_φ(m) ≡ (a * m_1)(a * m_2) … (a * m_φ(m)) (mod m)。我為什么要順便說一下Euler定理呢?因?yàn)橄旅嬉痪湓捒梢栽黾游揖W(wǎng)站的PV:這個(gè)定理出現(xiàn)在了The Hundred Greatest Theorems里。
  談到Fermat小定理,數(shù)學(xué)歷史上有很多誤解。很長一段時(shí)間里,人們都認(rèn)為Fermat小定理的逆命題是正確的,并且有人親自驗(yàn)證了 a=2, p<300的所有情況。國外甚至流傳著一種說法,認(rèn)為中國在孔子時(shí)代就證明了這樣的定理:如果n整除2^(n-1)-1,則n就是素?cái)?shù)。后來某個(gè)英 國學(xué)者進(jìn)行考證后才發(fā)現(xiàn)那是因?yàn)樗麄兎g中國古文時(shí)出了錯(cuò)。1819年有人發(fā)現(xiàn)了Fermat小定理逆命題的第一個(gè)反例:雖然2的340次方除以341余 1,但341=11*31。后來,人們又發(fā)現(xiàn)了561, 645, 1105等數(shù)都表明a=2時(shí)Fermat小定理的逆命題不成立。雖然這樣的數(shù)不多,但不能忽視它們的存在。于是,人們把所有能整除2^(n-1)-1的合 數(shù)n叫做偽素?cái)?shù)(pseudoprime),意思就是告訴人們這個(gè)素?cái)?shù)是假的。
  不滿足2^(n-1) mod n = 1的n一定不是素?cái)?shù);如果滿足的話則多半是素?cái)?shù)。這樣,一個(gè)比試除法效率更高的素性判斷方法出現(xiàn)了:制作一張偽素?cái)?shù)表,記錄某個(gè)范圍內(nèi)的所有偽素?cái)?shù),那么 所有滿足2^(n-1) mod n = 1且不在偽素?cái)?shù)表中的n就是素?cái)?shù)。之所以這種方法更快,是因?yàn)槲覀兛梢允褂枚址焖儆?jì)算2^(n-1) mod n 的值,這在計(jì)算機(jī)的幫助下變得非常容易;在計(jì)算機(jī)中也可以用二分查找有序數(shù)列、Hash表開散列、構(gòu)建Trie樹等方法使得查找偽素?cái)?shù)表效率更高。
   有 人自然會關(guān)心這樣一個(gè)問題:偽素?cái)?shù)的個(gè)數(shù)到底有多少?換句話說,如果我只計(jì)算2^(n-1) mod n的值,事先不準(zhǔn)備偽素?cái)?shù)表,那么素性判斷出錯(cuò)的概率有多少?研究這個(gè)問題是很有價(jià)值的,畢竟我們是OIer,不可能背一個(gè)長度上千的常量數(shù)組帶上考場。 統(tǒng)計(jì)表明,在前10億個(gè)自然數(shù)中共有50847534個(gè)素?cái)?shù),而滿足2^(n-1) mod n = 1的合數(shù)n有5597個(gè)。這樣算下來,算法出錯(cuò)的可能性約為0.00011。這個(gè)概率太高了,如果想免去建立偽素?cái)?shù)表的工作,我們需要改進(jìn)素性判斷的算 法。 
   最簡單的想法就是,我們剛才只考慮了a=2的情況。對于式子a^(n-1) mod n,取不同的a可能導(dǎo)致不同的結(jié)果。一個(gè)合數(shù)可能在a=2時(shí)通過了測試,但a=3時(shí)的計(jì)算結(jié)果卻排除了素?cái)?shù)的可能。于是,人們擴(kuò)展了偽素?cái)?shù)的定義,稱滿足 a^(n-1) mod n = 1的合數(shù)n叫做以a為底的偽素?cái)?shù)(pseudoprime to base a)。前10億個(gè)自然數(shù)中同時(shí)以2和3為底的偽素?cái)?shù)只有1272個(gè),這個(gè)數(shù)目不到剛才的1/4。這告訴我們?nèi)绻瑫r(shí)驗(yàn)證a=2和a=3兩種情況,算法出錯(cuò) 的概率降到了0.000025。容易想到,選擇用來測試的a越多,算法越準(zhǔn)確。通常我們的做法是,隨機(jī)選擇若干個(gè)小于待測數(shù)的正整數(shù)作為底數(shù)a進(jìn)行若干次 測試,只要有一次沒有通過測試就立即把這個(gè)數(shù)扔回合數(shù)的世界。這就是Fermat素性測試。
  人們自然會想,如果考慮了所有小于n的底數(shù)a,出錯(cuò)的概率是否就可以降到0呢?沒想 到的是,居然就有這樣的合數(shù),它可以通過所有a的測試(這個(gè)說法不準(zhǔn)確,詳見我在地核樓層的回復(fù))。Carmichael第一個(gè)發(fā)現(xiàn)這樣極端的偽素?cái)?shù),他 把它們稱作Carmichael數(shù)。你一定會以為這樣的數(shù)一定很大。錯(cuò)。第一個(gè)Carmichael數(shù)小得驚人,僅僅是一個(gè)三位數(shù),561。前10億個(gè)自 然數(shù)中Carmichael數(shù)也有600個(gè)之多。Carmichael數(shù)的存在說明,我們還需要繼續(xù)加強(qiáng)素性判斷的算法。
  Miller和Rabin兩個(gè)人的工作讓Fermat素性測試邁出了革命性的一步,建立了傳說中的Miller-Rabin素性測試算法。 新的測試基于下面的定理:如果p是素?cái)?shù),x是小于p的正整數(shù),且x^2 mod p = 1,那么要么x=1,要么x=p-1。這是顯然的,因?yàn)閤^2 mod p = 1相當(dāng)于p能整除x^2-1,也即p能整除(x+1)(x-1)。由于p是素?cái)?shù),那么只可能是x-1能被p整除(此時(shí)x=1)或x+1能被p整除(此時(shí) x=p-1)。
   我們下面來演示一下上面的定理如何應(yīng)用在Fermat素性測試上。前面說過341可以通過以2為底的Fermat測試,因 為2^ 340 mod 341=1。如果341真是素?cái)?shù)的話,那么2^ 170 mod 341只可能是1或340;當(dāng)算得2^ 170 mod 341確實(shí)等于1時(shí),我們可以繼續(xù)查看2^ 85除以341的結(jié)果。我們發(fā)現(xiàn),2^ 85 mod 341=32,這一結(jié)果摘掉了341頭上的素?cái)?shù)皇冠,面具后面真實(shí)的嘴臉顯現(xiàn)了出來,想假扮素?cái)?shù)和我的素MM交往的企圖暴露了出來。
   這就 是Miller-Rabin素性測試的方法。不斷地提取指數(shù)n-1中的因子2,把n-1表示成d*2^ r(其中d是一個(gè)奇數(shù))。那么我們需要計(jì)算的東西就 變成了a的d*2^ r次方除以n的余數(shù)。于是,a^(d * 2^ (r-1))要么等于1,要么等于n-1。如果a^(d * 2^ (r-1))等于1,定理繼續(xù)適用于a^(d * 2^ (r-2)),這樣不斷開方開下去,直到對于某個(gè)i滿足a^(d * 2^i) mod n = n-1或者最后指數(shù)中的2用完了得到的a^d mod n=1或n-1。
  這樣,F(xiàn)ermat小定理加強(qiáng)為如下形式:
   盡可能提取因子2, 把n-1表示成d*2^r ,如果n是一個(gè)素?cái)?shù),那么或者a^d mod n=1,或者存在某個(gè)i使得a^ (d*2^ i) mod n=n-1 ( 0<=i<r ) (注意i可以等于0,這就把a(bǔ)^d mod n=n-1的情況統(tǒng)一到后面去了)
   Miller-Rabin 素性測試同樣是不確定算法,我們把可以通過以a為底的Miller-Rabin測試的合數(shù)稱作以a為底的強(qiáng)偽素?cái)?shù)(strong pseudoprime)。第一個(gè)以2為底的強(qiáng)偽素?cái)?shù)為2047。第一個(gè)以2和3為底的強(qiáng)偽素?cái)?shù)則大到1 373 653。
  Miller- Rabin算法的代碼也非常簡單:計(jì)算d和r的值(可以用位運(yùn)算加速),然后二分計(jì)算a^d mod n的值,最后把它平方r次。
素?cái)?shù)的性質(zhì)。

Miller-Rabin質(zhì)數(shù)測試
模板來源:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
using namespace std;
//  計(jì)算(a * b) % mod
long long multiMod(long long a,long long b,long long mod)
{
    long long res=0;
    while(b)
    {
        if(b&1)
        {
            res=res+a;
            if(res>=mod) res-=mod;
        }
        a=a+a;
        if(a>=mod) a-=mod;
        b>>=1;
    }
    return res;
}
//  計(jì)算 (a ^ b) % mod
long long powMod(long long a,long long b,long long mod)
{
    long long res=1;
    while(b)
    {
        if(b&1)
        {
            res=multiMod(res,a,mod);
        }
        a=multiMod(a,a,mod);
        b>>=1;
    }
    return res;
}
//  通過a ^ (n - 1) = 1(mod n)來判斷n是不是素?cái)?shù)
//  n - 1 = x * 2 ^ t中間使用二次判斷
//  是合數(shù)返回true,不一定是合數(shù)返回false
bool check(long long a,long long n,long long x,int t)
{
    long long res=powMod(a,x,n);
    long long last=res;
    for(int i=1;i<=t;i++)
    {
        res=multiMod(res,res,n);
        if(res==1&&last!=1&&last!=n-1) return true;//合數(shù)
        last=res;
    }
    return res!=1;//最后res=(a^(n-1) % n),如果res!=1,那么不滿足費(fèi)小馬定理,說明不是素?cái)?shù)
}
//  生成[ 0 , n ]的隨機(jī)數(shù)
long long randomVal(long long n)
{
    //rand(): 0~RAND_MAX;
    return ((double)rand()/RAND_MAX*n+0.5);
}
//  隨機(jī)算法判定次數(shù),一般8~10次就夠了
const int times=8;
//  Miller_Rabin算法
//  是素?cái)?shù)返回true,(可能是偽素?cái)?shù))
//  不是素?cái)?shù)返回false
bool Miller_Rabin(long long n)
{
    if(n<2) return false;
    if(n==2) return true;
    if(!(n&1)) return false;//  偶數(shù)(合數(shù))
    long long x=n-1,t=0;
    while((x&1)==0)
    {
        t++;
        x>>=1;
    }
    for(int i=0;i<times;i++)
    {
        long long a=randomVal(n-2)+1;
        if(check(a,n,x,t)) return false;
    }
    return true;
}
int main()
{
    long long n,t;
    scanf("%lld",&t);
    while(t--)
    {
        scanf("%lld",&n);
        if(Miller_Rabin(n)) printf("Yes\n");
        else printf("No\n");
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,748評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,165評論 3 414
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,595評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,633評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,435評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,943評論 1 321
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,035評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,175評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,713評論 1 333
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,599評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,788評論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,303評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,034評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,412評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,664評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,408評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,747評論 2 370

推薦閱讀更多精彩內(nèi)容