背景
在十二省联考中突然忘记 Miller-Rabin 怎么写,决定滚回来写这篇博客
费马小定理
假设 $x$ 是一个整数,$p$ 是一个质数,那么满足
$$
x^{p-1} \equiv 1 \pmod p
$$
这个定理也是求模质数逆元的依据,即
$$
x^{p-2} \times x \equiv x^{p-1} \equiv 1 \pmod p
$$
因此我们可以随机一些数 $x$ ,判断 $x^{p-1}$ 对 $p$ 取模是否同余于 $1$,如果不是就是一个合数
这种方法已经可以正确判断很多数了,但有一些具有特殊性质的数依旧无法判断
二次探测定理
假设存在一个质数 $p$ 和一个整数 $x$
$$
x^2 \equiv 1 \pmod p
$$
那么必定满足 $x \equiv 1 \pmod p$ 或 $x \equiv p – 1 \pmod p$
那么我们可以将 $p-1$ 写成 $2^u \cdot v$ 的形式,枚举一个大于 $1$ 小于 $p$ 的正整数 $a$
判断
$$
a^v, a^{2v}, a^{2^2v} \cdots, a^{2^{u-1}v}
$$
中是否有模 $p$ 同余于 $p-1$ 的数,有说明 $p$ 有可能是一个质数,没有就不是一个质数
这个算法的正确率是 $\frac{3}{4}$ ,因此我们可以随机 $k$ 个数用于判定,正确率是 $1 – \left( \frac{1}{4} \right)^k$
当 $k$ 大于等于 $10$ 时,算法错误率小于 $0.00001\%$
代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <ctime>
#include <algorithm>
using namespace std;
const int TIMES = 10;
inline long long mul(long long a, long long b, long long mod){
long long ans = 0;
for(; b; b >>= 1){
if(b & 1) ans = (ans + a) % mod;
a = (a + a) % mod;
}
return ans;
}
inline long long powx(long long a, long long b, long long mod){
long long ans = 1;
for(; b; b >>= 1){
if(b & 1) ans = mul(ans, a, mod);
a = mul(a, a, mod);
}
return ans;
}
inline bool check(long long a, long long x){
long long tmp = x - 1;
int k = 0;
while(!(tmp & 1)){
tmp >>= 1;
k++;
}
tmp = powx(a, tmp, x);
if(tmp == 1) return true;
for(register int i=1; i<=k; i++){
if(tmp == x - 1) return true;
tmp = mul(tmp, tmp, x);
}
return false;
}
inline bool miller_rabin(long long x){
if(x < 2) return false;
if(x == 2 || x == 3 || x == 5 || x == 7) return true;
if(x % 2 == 0 || x % 3 == 0 || x % 5 == 0 || x % 7 == 0) return false;
for(register int i=1; i<TIMES; i++){
long long a = rand() % (x - 2) + 2;
if(!check(a, x)) return false;
}
return true;
}