1. 程式人生 > >Miller-Rabin素性測試與二次探測

Miller-Rabin素性測試與二次探測

演算法簡介

首先是一些概念:

  • 費馬小定理:對於素數p和任意整數a,有apa(modp).
  • 反之,對於一個數p,如果滿足apa(modp),則 p 很可能是素數。
  • 偽素性測試:瞎猜若干個x,只要不滿足上式,那麼p就不是素數。看起來沒毛病了。
  • Carmichael數:對於合數n,如果對所有正整數b(b和n互素)都有bn11(modn)成立,則合數n為Carmichael數。比如561。這種數的存在使得上面的方法淪為“偽素性測試”。
  • 二次探測定理:如果p是奇素數,則 x21(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; }