Miller Rabin素性检验是一种素数判定的法则,由CMU的教授Miller首次提出,并由希大的Rabin教授作出修改,能够在O(k*logn^3)的时间内算出一个数是不是素数,但是有一定的错误概率
根据检测的次数n,可以把错误概率降低到 (1/4)^n 实际上比这个还要小
在int范围内,选取 {2,7,61} 三个数作为底数可以保证100%正确。
在long long范围内,选取 {2,325,9375,28178,450775,9780504,1795265022} 七个数作为底数可保证100%正确。
依据原理:
- 费马小定理
- 有限域 Fp 上对1开方只能得到1或-1,n为合数时,对1开方可以开出其他的结果
原理理解:
由Fermat检测得 ≡1(mod p)。
将其拆分为 ≡1(mod p)。用二次检测定理来判断.
≡1(mod p) 或者 ≡-1(mod p),同样的我们可以递推下去到,
也就是说,我们可以将奇数n表示成,对, , ... 这一系列数进行检验,这一系列数进行检验,他们的解要么全是1,要么出现 −1 后全是1(之前不能有1),否则就不是素数。当然,要注意 −1 不能出现在最后一个数,否则就连费马小定理都不满足了。
具体计算过程:
总结一下,该检验的思路是:
1)先特判筛掉3以下的数与偶数。
2)将待检验数 n, n−1 化为
3)选取多个底数,分别对, , … 进行检验,判断其解是否全为1,或在非最后一个数的情况下出现 p−1,然后之后全是1 。
4)如果都满足,我们就认为这个数是素数。
样例代码:
eg1.
bool MRtest(ll n)//Miller Rabin Test
{
if(n<3||n%2==0) return n==2;//特判
ll u=n-1,t=0;
while(u%2==0) u/=2,++t;
ll ud[]={2,325,9375,28178,450775,9780504,1795265022};
for(ll a:ud)
{
ll v=qpow(a,u,n);
if(v==1||v==n-1) continue; // 第一步就得到了1 or p-1 可以直接跳过后面的循环
for(int j=1;j<=t;j++)
{
v=qmul(v,v,n);
if(v==n-1&&j!=t){v=1;break;}//出现一个n-1,后面都是1,直接跳出
if(v==1) return 0;//这里代表前面没有出现n-1这个解,二次检验失败
}
if(v!=1) return 0;//Fermat检验
}
return 1;
}
eg2.
bool Miller_Rabin(ll n)
{
int m = n-1;
int t = 0;
if (n==2)
{
return true;
}
else if (n<2 || !(n&1))
{
return false;
}
//求出n-1 = m*2^t的m和t。
while (!(m&1))
{
m>>=1;
t++;
}
for (int i=0; i<20; i++)
{
//随机数a
ll a = rand()%(n-1) + 1;
// 计算a^m
ll x = qul_pow(a, m, n), y;
for (int j=0; j<t; j++)
{
y = qul_mul(x, x, n); //进行(x*x)%n操作。
//不满足二次探测定理,也就是y得1了但是x并不等于1或者n-1,那么n就一定不是质数。
if (y==1 && x!=1 && x!=n-1)
{
return false;
}
x = y;
}
//上面循环跑完了,如果最后x都不等于1,那么一定是一个合数了。
if (x!=1)
{
return false;
}
}
return true;
}
eg1和eg2是主流的两种代码, 其实都是一样的.
参考资料:
【朝夕的ACM笔记】数论-Miller Rabin素数判定 - 知乎 (zhihu.com)