Miller-Rabin學習筆記
\(Miller-Rabin\)
\(Miller-Rabin\) 用於判定一個大整數是不是素數,且速度非常快
應該是 \(O(klog^3n)\),其中 \(k\) 為測試的次數,\(n\) 為要判定的數
演算法本質上是一種概率演算法,存在誤判的可能性,但是出錯的概率非常小。出錯的概率到底是多少,存在嚴格的理論推導。(網上copy的)
兩個重要的東西
\(Farmet\)測試
根據費馬小定理,如果 \(p \in \mathbb P\) 且 \(\gcd(a , p) = 1\) 那麼 \(a^{p-1} \equiv 1 \pmod p\)。它給我們提供了一種檢驗素數的思路:對於數n,它的基本思想是不斷地選取在 \([2,n-1]\) 的數讓它為 \(a\) 並檢驗是否每次都有 \(a^{n-1} \equiv 1 \pmod n\),如果沒有,那麼 \(n\) 肯定不是素數
其實有它我們就可以大概率判定素數了
為什麼是大概率呢?
誰告訴你費馬小定理的逆定理是成立的?
也就是說費馬小定理的逆定理並不成立,換言之,滿足了 \(a^{n-1} \equiv 1 \pmod n\),\(n\) 也不一定是素數。
例子:1819年有人發現了費馬小定理逆命題的第一個反例:雖然2的340次方除以341餘1,但341=11*31,後來,人們又發現了561, 645, 1105等數都表明a=2時Fermat小定理的逆命題不成立。雖然這樣的數不多,但不能忽視它們的存在。統計表明,在前10億個自然數中共有50847534個素數,而滿足 \(2^{n-1} ≡ 1 \pmod n\)
如果用費馬小定理的逆命題來判斷一個正整數n是不是素數,在前10億個自然數中出錯的可能性為 0.011%
這個出錯的可能性還是很高的,但是仍然可以用這個技巧來排除大量的合數,這種方法就是費馬檢測
上面的做法中隨機地選擇 \(a\) ,很大程度地降低了犯錯的概率。但是仍有一類數,上面的做法並不能準確地判斷。
對於合數 \(n\) ,如果對於所有正整數 \(a\) , \(a\) 和 \(n\) 互素,都有同餘式 \(a^{n-1} ≡ 1 \pmod n\) 成立,則合數 \(n\) 為卡邁克爾數(\(\texttt{Carmichael Number}\)),又稱為費馬偽素數。
比如, \(561 = 3 \times 11 \times 17\)
而且我們知道,若 \(n\) 為卡邁克爾數,則 \(m = 2^n-1\) 也是一個卡邁克爾數,從而卡邁克爾數的個數是無窮的。
於是 \(Farmet\) 測試變得很玄學
於是我們可以愉快清楚第二位 \(boss\)
https://www.cnblogs.com/fzl194/p/9046117.html
http://oi-wiki.com/math/prime/
二次探測定理
Code
#include<cstdio>
#include<ctime>
#include<algorithm>
using namespace std;
typedef long long LL;
inline LL fmul(LL x , LL y , LL p)
{
LL res = 0;
while (y)
{
if (y & 1) res = (res + x) % p;
y >>= 1 , x = (x + x) % p;
}
return res;
}
inline LL fpow(LL x , LL y , LL p)
{
LL res = 1;
while (y)
{
if (y & 1) res = fmul(res , x , p);
y >>= 1 , x = fmul(x , x , p);
}
return res;
}
inline int Miller_Rabin(LL m , int test_time)
{
if (m < 3) return (m == 2);
if (!(m & 1)) return 0;
LL d = m - 1;
int b = 0;
while (!(d & 1)) d = d >> 1 , b++;
srand((unsigned)time(NULL));
for(register int i = 0; i < test_time; i++)
{
LL a = rand() % (m - 3) + 2 , v = fpow(a , d , m) , u = 0;
for(register int j = 0; j < b; j++)
{
u = fmul(v , v , m);
if (u == 1 && v != 1 && v != (m - 1)) return 0;
v = u;
}
if (v != 1) return false;
}
return 1;
}
int main()
{
int n;
scanf("%d" , &n);
LL m;
while(n--)
{
scanf("%lld" , &m);
if (Miller_Rabin(m , 6)) printf("Prime\n");
else printf("Not prime\n");
}
}