轉(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");
}
}