用到两个定理:
-
费马小定理
-
二次探测定理
如果 p p p 是一个素数, 0 < x < p 0<x<p 0<x<p,则方程 x 2 ≡ 1 ( m o d p ) x^2≡1(mod p) x2≡1(modp) 的解为 x = 1 x=1 x=1 或 x = p − 1 x=p-1 x=p−1。
对于待检测数 p p p 在 [ 2 , p ] [2,p] [2,p] 中随机选取 s s s 次 a a a 判断 a ( p − 1 ) ≡ 1 ( m o d p ) a(p-1) ≡ 1(mod p) a(p−1)≡1(modp) 是否成立 一旦发现不成立则可判定 p p p 不是素数 为了提高正确的概率在求 a ( p − 1 ) a(p-1)%p a(p−1) 的过程中用到定理二 令 p − 1 = 2 t ∗ k p-1 = 2^t * k p−1=2t∗k (其中k是奇数) 然后 a ( p − 1 ) a(p-1) a(p−1) 就相当于 a k ∗ 2 t ak*2^t ak∗2t 也就是将 a k ak ak 平方t次 用 x [ i ] x[i] x[i] 来表示平方i次的结果 如果发现 x [ i ] = 1 x[i] = 1 x[i]=1 而 x [ i − 1 ] x[i-1] x[i−1] 既不等于 1 1 1 也不等于 p − 1 p-1 p−1 那么就可以判定 p p p 不是素数了 求 a ( p − 1 ) a(p-1)%p a(p−1) 的过程用的是快速幂 在用快速幂的过程中还用到了一个大数相乘取模的优化 即化乘法为加法 即模拟两个数相乘的二进制运算过程 每次相加取模 如果 p p p 成功通过检测则判定为素数否则为合数。
Code:
ll x[1005];
ll multi(ll a, ll b, ll p)
{ // 化乘法为加法
ll temp = 0;
while (b)
{
if (b & (ll)1)
temp = (temp + a) % p;
a = (a << 1) % p;
b >>= 1;
}
return temp;
}
ll qpow(ll a, ll b, ll p)
{ // 快速幂
ll temp = 1;
while (b)
{
if (b & (ll)1)
temp = multi(temp, a, p);
b >>= 1;
a = multi(a, a, p);
}
return temp;
}
int miller_rabin(ll n)
{
if (n == 0 || n == 1)
return 0;
if (n == 2)
return 1;
int s = 10;
ll k = n - 1;
int t = 0;
while (!(k & (ll)1))
{
t++;
k >>= 1;
}; // k如果不是奇数将k转化为奇数
while (s--)
{
ll a = rand() % (n - 2) + 2; // 随机取值
x[0] = qpow(a, k, n);
for (int i = 1; i <= t; i++)
{
x[i] = multi(x[i - 1], x[i - 1], n); // 记录平方i次的结果
if (x[i] == 1 && x[i - 1] != 1 && x[i - 1] != n - 1)
return 0;
}
if (x[t] != 1)
return 0;
}
return 1;
}