M i l l e r − r a b i n Miller-rabin Miller−rabin算法是一个用来快速判断一个正整数是否为素数的算法,它利用了费马小定理和二次探测:
费马小定理:如果 p p p是质数且 a ⊥ p a \perp p a⊥p互质,那么 a p − 1 ≡ 1 ( m o d p ) a^{p-1} \equiv 1 ~~ (mod~~ p) ap−1≡1 (mod p)恒等于 1 1 1。
也就是对于所有小于 p p p的正整数 a a a来说都应该符合 a p − 1 ( m o d p ) a^{p-1}~~(mod~~p) ap−1 (mod p)恒等于 1 1 1。那么根据逆否命题,对于一个 p p p,我们只要举出一个 a ( a < p ) a(a a(a<p)
二次探测定理:如果 p p p是一个素数,那么对于正整数 x ( x < p ) x(x x(x<p) x(x<p)
根据这个定理,我们要计算 a p − 1 m o d p a^{p-1}~~ mod~~ p ap−1 mod p是否等于 1 1 1时,可以这样计算,设 p − 1 = k ∗ 2 t p-1=k*2^t p−1=k∗2t,从 a k a^k ak开始,不断将其平方直到得到 a p − 1 a^{p-1} ap−1,一旦发现某次平方并对 p p p取模后等于 1 1 1,那么说明符合了二次探测定理的逆否命题使用条件,立即检查 x x x是否等于 1 1 1或 p − 1 p-1 p−1,如果都不是则可直接判定 p p p为合数
inline ll mul(ll a, ll b, ll p) {
//wa了尝试慢速乘或者改为__int128
if (p <= 1000000000) return a * b % p;
else if (p <= 1000000000000LL) return (((a * (b >> 20) % p) << 20) + (a * (b & ((1 << 20) - 1)))) % p;
else {
ll d = (ll) floor(a * (long double) b / p + 0.5);
ll ret = (a * b - d * p) % p;
if (ret < 0) ret += p;
return ret;
}
}
//inline ll mul(ll x, ll n, ll p) {
// ll ans = 0;
// x %= p;
// while (n) {
// if (n & 1) ans = (ans + x) % p;
// x = (x + x) % p;
// n >>= 1;
// }
// return ans;
//}
inline ll qkp(ll x, ll n, ll p) {
ll ans = 1;
x %= p;
while (n) {
if (n & 1) ans = mul(ans, x, p);
x = mul(x, x, p);
n >>= 1;
}
return ans;
}
bool miller_rabin(ll n, int t = 20) {
if (n == 2) return true;
if (n < 2 || !(n & 1)) return false;
ll m = n - 1;
int k = 0;
while ((m & 1) == 0) {
m >>= 1;
k++;
}
for (int i = 0; i < t; i++) {
ll a = rand() % (n - 1) + 1;
ll x = qkp(a, m, n), y = 0;
for (int j = 0; j < k; j++) {
y = mul(x, x, n);
if (y == 1 && x != 1 && x != n - 1) return false;
x = y;
}
if (y != 1) return false;
}
return true;
}
J a v a Java Java的 B i g I n t e g e r BigInteger BigInteger类封装了大数的米勒罗宾判素数的方法:
import java.math.BigInteger;
import java.util.*;
public class Main{
public static void main(String[] args){
BigInteger x;
Scanner in = new Scanner(System.in);
x = in.nextBigInteger();
if(x.isProbablePrime(50)) //相当于该数是素数的概率超过99%
System.out.println("Yes");
else
System.out.println("No");
}
}