Miller-Rabin素性測試與二次探測
阿新 • • 發佈:2019-01-03
演算法簡介
首先是一些概念:
- 費馬小定理:對於素數p和任意整數a,有
ap≡a(modp) . - 反之,對於一個數p,如果滿足
ap≡a(modp) ,則 p 很可能是素數。 - 偽素性測試:瞎猜若干個x,只要不滿足上式,那麼p就不是素數。看起來沒毛病了。
- Carmichael數:對於合數n,如果對所有正整數b(b和n互素)都有
bn−1≡1(modn) 成立,則合數n為Carmichael數。比如561。這種數的存在使得上面的方法淪為“偽素性測試”。 - 二次探測定理:如果p是奇素數,則
x2≡1(modp) 的解為x = 1或x = p - 1(mod p),這是由模運算的迴圈特性導致的。
利用二次探測定理,只需要探測s次就可以將錯誤率降到2^(-s)(好像是這樣吧。。反正很低就對了),因此也不會多花多少時間。
記得判素時的細節處理以及快速冪取模。
程式碼
應該是最簡潔的模板了。。
#include <cstdio>
using namespace std;
typedef long long ll;
ll prime[5] = {2, 3, 5, 233, 331};
ll pow_mod(ll a, ll n, ll mod)
{
ll ret = 1;
while (n) {
if (n & 1) ret = ret * a % mod;
a = a * a % mod;
n >>= 1;
}
return ret;
}
int miller_rabin(ll n)
{
if (n < 2 || (n != 2 && !(n & 1))) return 0;
ll s = n - 1;
while (!(s & 1)) s >>= 1;
for (int i = 0; i < 5; ++i) {
if (n == prime[i]) return 1;
ll t = s, m = pow_mod(prime[i], s, n);
while (t != n - 1 && m != 1 && m != n - 1) {
m = m * m % n;
t <<= 1;
}
if (m != n - 1 && !(t & 1)) return 0;
}
return 1;
}
int main()
{
ll n;
while (~scanf("%lld", &n))
printf("%s\n", miller_rabin(n) ? "YES" : "NO");
return 0;
}