在介紹素數測試相關演算法之前,先要引入尤拉定理和費馬定理。
尤拉定理:對於任意的整數n>1,a^φ(n) ≡1(mod n)對所有的a∈z*n都成立。
其中φ(n)為z*n的規模。φ(n)=n∏(1-1/p).可以理解成初始有一張的表,然後對每個能整除n的p,在表中刪除p的倍數後剩下的數。z*n定義為中與n互質的元素。例如:z*14=,φ(14)=5。
我們考慮的乙個已知元素a對模n的倍數,,考慮對模a的冪組成的序列,其中a∈z* n:
a0,a1,a2,a3,……
模n.第i個數為ai mod n.
例如:對模7,3的冪為
i 0 1 2 3 4 5 6 7……
3^i % 7 1 3 2 6 4 5 1 3……
則z*7中,<3>=,則φ(7)=6同樣可以得到<1>=,<2>=.
由尤拉定理我們可以從而得出費馬定理,他是尤拉定理的特殊表現。
費馬定理:如果p是素數,則a^(p-1) ≡1(mod p)對所有的a∈z*p都成立。
當p為素數時,他的因子只有p和1. 根據φ(n)=n∏(1-1/p):
φ(p)=p(1-1/p1)..(1-1/pn)=p-1
由尤拉定理即可驗證費馬定理的正確性。
對於素數測試有一種簡便的方法,我們稱之為試除法。就是我們用2到sqrt(n)分別去除n,每次的試除需要常數時間,最壞的情況需要的時間是⊙(sqrt(n)),n為長度。所以只有n很小或者n有小因子的時候該演算法才能較快的執行。
下面介紹一種效率較高的演算法,miller-rabin隨機素數測試法。他是對於偽素數的改進。
所謂偽素數的定義為:z+ n表示z n中的非零元素:
z+ n=
如果n是乙個合數,且滿足 an-1≡1(mod n)則說n是基於a的偽素數。例如:341,561(基於2)。
根據費馬定理如果p是素數,則ap-1 ≡1(mod p)對所有的a∈z*p都成立。
我們可以思考,如果要判斷乙個數p是素數,那麼我們是不是只要隨機找乙個a其中p>a>1,如果a^p-1 ≡1(mod p),那麼p有可能是素數了。如果對所有的a∈z*p都成立,那麼p必然是素數。反過來若發現存在乙個a∈z*p 使得ap-1 !≡1(mod p)則p必然不是素數。我們的這個演算法的思想就是使用隨機的a去探測是否存在這樣的乙個a使得a^(p-1) !≡1(mod p),如果存在這樣的a,那麼就皆大歡喜,你已經證明了他不是素數了。如果沒找到,那麼他極有可能是素數了,但不一定是。
偽**:
pseudoprime(n)
1 if(an-1mod n!≡1)
2 return composite.
3 else return prime.
該過程如果結果為composite則可以認定n不為素數。如果結果返prime則極大可能為素數。如果判定為合數結果總是正確的。如果判定為素數的話,只有當n是基於a的偽素數才會出錯。這個過程出錯的機率相當的小,選取512位數,基於2的偽素數概率不到1/1020.在實際的操作中幾乎永遠不會出錯。
miller-rabin隨機素數測試法對偽素數測試過程進行了兩方面的改進:
第一:他實驗了數個隨機選取的a進行試驗。
第二:當計算每個模取冪的值時,注意在最後一組平方里是否發現了對模n來說1的非平方根。如果存在,終止並輸出pomposite.
下面證明第二點:
定理:如果p是乙個奇素數且e≥1.則方程
x2≡1(mod pe)
僅有兩個解: x1=1和x2=-1。
由定理可以得出推論:如果對模n存在1的非平凡平方根,則n是合數。(平凡平方根為1或-1)。因此檢測非平凡平方根的存在可以有效的判斷n是合數。把它運用與miller-rabin素數測試過程。令n-1=2tu,其中t≥1且u是奇數;亦即,n-1的二進位制表示是奇數u的二進位制後面跟上t個零。例如n=13,n-1的二進位制為1100,則t=2,u=3.因此,an-1≡(a u)2^t,所以可以通過先計算a u mod n,然後對結果連續平方t次來計算an-1 mod n。
偽**:
witness(a,n)
1 let n-1=2tu,where t≥1 and u is odd
2 x0=au mod n
3 for i←1 to t
4 do xi=x2 i-1 mod n
5 if xi=1 and x i-1≠1and x i-1≠n-1
6 then return true
7 if(xt≠1)
8 then return true
9 return false
該演算法通過第2行計算x0=au mod n。3到6行對結果進行t次平方,計算出an-1 mod n.
5到6行進行非平凡平方根的檢測,如發現非平凡平方根則證明是合數,返回true。第7到8行為檢測an-1 mod n是否為1,如果發現不為1則不滿足素數an-1 mod n≡1,證明n是合數返回true,若沒發現能證明n是合數的證據,則將會執行到第9行結束返回false.
多次試驗可以有效的降低錯誤率的發生。例如:當a=2時561是偽素數,他將被當做素數處理。而在a=7的情況下,a560≡241(mod 561)因此a=7可以證明561為合數。
偽**:
miller-rabin(n,s)
1for i=1 to s
2 do a=random(1,n-1)
3 if witness (a,n)
4 the return composite//確定為合數
5 return prime
第1到4行進行s次實驗,取a為1到n-1之間的隨機數。第三行進行判斷是否存在乙個值能證明n是合數,如果返回值為true說明找到了n為合數定的證據,n必然是合數。返回false找不到,則說明n極大可能是乙個素數。
miller rabin是乙個運氣演算法。他出錯的概率與你選擇的s的大小以及隨機數a取到的值有關。只有當運氣太差,在主迴圈s次迭代中,每一次都沒能發現n為合數的證據時,才會出錯,每次都錯過發現證據的概率至多為2-s,在實際執行中出錯的機率是相當低的。 附:
intwitness(
inta,
intn)
return
(d ==1?
0: 1);
}int
miller(
intn,
ints =50
)return1;
}
又見素數(素數篩法)
又見素數 time limit 2000ms memory limit 65536k total submit 287 accepted 24 description 給定乙個正整數n n 10000000 求n 包含 以內素數的個數。input 第一行為乙個整數t t 1000 表示測試資料的組數...
素數篩法求素數
素數篩類似於打表標記,預先處理掉非素數的數,即素數的倍數 任意非素數都可以由幾個素數相乘得到 於是效率比暴力求解快得多。埃氏篩法的效率為o n loglog n 簡單易懂,但是會重複標記,比如當i為2時,6會被標記掉,然而當i為3時,6又會被重複標記,這樣的重複訪問加大了時間複雜度,於是有了尤拉篩。...
驗證素數(素數篩選法驗證素數)
本來是在做容斥原理的題,既然碰到求a b內與n互質的數的個數,就順便將與素數相關的知識整理一遍。1 輸入數n,判斷是否為素數的一般方法 includeusing namespace std int main cout includeusing namespace std int main cout ...