二、Lambda表示式
Miller-Rabin質數測試
資料源:
Miller-Rabin素數測試演算法——forever_dreams
hihoCoder #1287
正文:
非精確演算法警告
作用
當我們需要判斷素數時,如果樸素\(O(\sqrt{n})\)演算法不能通過,那麼可以通過Miller-Rabin素數測試,來大概率地判斷其是否為素數。
兩個基礎理論
(1)費馬小定理:當 p 為質數,有 \(a^{p-1}\equiv 1(mod\ p)\) ,不過反過來不一定成立,也就是說,如果 a , p 互質,且 \(a^{p-1}\equiv 1(mod\ p)\) ,不能推出 p 是質數,比如\(Carmichael\)
(2)二次探測:如果 p 是一個素數,0 < x < p, 則方程 \(x^{2}\equiv 1(mod\ p)\) 的解為 x = 1 或 x = p - 1
兩個基本理論的證明
小Hi:這種質數演算法是基於費馬小定理的一個擴充套件,首先我們要知道什麼是費馬小定理:
費馬小定理:對於質數p和任意整數a,有a^p ≡ a(mod p)(同餘)。反之,若滿足a^p ≡ a(mod p),p也有很大概率為質數。
將兩邊同時約去一個a,則有a^(p-1) ≡ 1(mod p)
也即是說:假設我們要測試n是否為質數。我們可以隨機選取一個數a,然後計算a^(n-1) mod n,如果結果不為1,我們可以100%斷定n不是質數。
否則我們再隨機選取一個新的數a進行測試。如此反覆多次,如果每次結果都是1,我們就假定n是質數。
該測試被稱為Fermat測試。需要注意的是:Fermat測試不一定是準確的,有可能出現把合數誤判為質數的情況。
Miller和Rabin在Fermat測試上,建立了Miller-Rabin質數測試演算法。
與Fermat測試相比,增加了一個二次探測定理:
如果p是奇素數,則 x^2 ≡ 1(mod p)的解為 x ≡ 1 或 x ≡ p - 1(mod p)
如果a^(n-1) ≡ 1 (mod n)成立,Miller-Rabin演算法不是立即找另一個a進行測試,而是看n-1是不是偶數。如果n-1是偶數,另u=(n-1)/2,並檢查是否滿足二次探測定理即a^u ≡ 1 或 a^u ≡ n - 1(mod n)。
舉個Matrix67 Blog上的例子,假設n=341,我們選取的a=2。則第一次測試時,2^340 mod 341=1。由於340是偶數,因此我們檢查2^170,得到2^170 mod 341=1,滿足二次探測定理。同時由於170還是偶數,因此我們進一步檢查2^85 mod 341=32。此時不滿足二次探測定理,因此可以判定341不為質數。
將這兩條定理合起來,也就是最常見的Miller-Rabin測試。
但一次MR測試仍然有一定的錯誤率。為了使我們的結果儘可能的正確,我們需要進行多次MR測試,這樣可以把錯誤率降低。
寫成虛擬碼為:
Miller-Rabin(n):
If (n <= 2) Then
If (n == 2) Then
Return True
End If
Return False
End If
If (n mod 2 == 0) Then
// n為非2的偶數,直接返回合數
Return False
End If
// 我們先找到的最小的a^u,再逐步擴大到a^(n-1)
u = n - 1; // u表示指數
while (u % 2 == 0)
u = u / 2
End While // 提取因子2
For i = 1 .. S // S為設定的測試次數
a = rand_Number(2, n - 1) // 隨機獲取一個2~n-1的數a
x = a^u % n
While (u < n)
// 依次次檢查每一個相鄰的 a^u, a^2u, a^4u, ... a^(2^k*u)是否滿足二次探測定理
y = x^2 % n
If (y == 1 and x != 1 and x != n - 1) // 二次探測定理
// 若y = x^2 ≡ 1(mod n)
// 但是 x != 1 且 x != n-1
Return False
End If
x = y
u = u * 2
End While
If (x != 1) Then // Fermat測試
Return False
End If
End For
Return True
值得一提的是,Miller-Rabin每次測試失誤的概率是1/4;進行S次後,失誤的概率是4^(-S)。
小Hi:那麼小Ho,你能計算出這個演算法的時間複雜度麼?
小Ho:恩,每一次單獨的MR測試,需要O(log n)的時間。一共要進行S次MR測試,也就是O(Slog n)。
小Hi:沒錯,這樣就能夠在很短的時間內完成質數的測試了。當然如果你還是不放心,可以把S的值設定的更高一點。
小Ho:好!這樣就能夠順利的找到大質數了。
本題的提示參考了Matrix67的Blog和wikipedia的詞條。
Matrix67的Blog有更多的細節描寫。Wiki中的虛擬碼比上文中的簡潔一些,並且有介紹了一些小技巧:比如如果n<2^64(n<1e18),只用選取a=2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37做測試即可