[学习笔记]Miller-Rabin素数检测
一.什么是Miller-Rabin
miller-rabin是一个用来检验一个数是不是素数的算法,速度非常的快,\(O(\log^2n)\)(加了快速加法)
二.费马小定理
对于素数p,有
\[ a^p\equiv a\;(mod\; p) \]
这是费马小定理的基本形式。可以变形成为\(a^{p-1}\equiv1(mod\;p)\)。对于费马小定理来说,大多数时候满足这个式子的一个自然数是一个质数,当然有满足却不是质数的情况。并且不满足他的一定不是质数,这是Miller-Rain算法的基本原理。也就是说,当我们用多个底数a检验一个自然数,发现都满足费马小定理,我们就大约可以认定他是一个质数。
对于这样的一类数p(以561为代表):\(\forall\;a\in Z,a^{p-1}\equiv1(mod\;p)\),我们称它为Carmichael数,普通的Miller-Rabin是会将他们误判为素数的。
三.二次探测定理
对于一个奇素数(除2以外的素数)来说,一个方程\(x^2\equiv1(mod\;p)\)的解为\(x\equiv1(mod\;p)\)或\(x\equiv p-1(mod\;p)\)
证:
\[ \begin{align} x^2\equiv1(mod\;p)\\ x^2-1\equiv0(mod\;p)\\ (x+1)(x-1)\equiv0(mod\;p) \end{align} \]
于是对于一个p-1为偶数的指数来说,我们就先把它用二次探测定理做一遍。令\(u=\frac{p-1}{2}\),对\(a^u\equiv1(mod\;p)\),\(a^u\equiv p-1(mod\;p)\),进行判定。
四.后记
以上的两种方法结合起来应该就可以进行正确的检验素数,但然也可以先把几个常用的素数:2,3,5...17等先判定一遍以此来减小出错概率(笔者还没有测过)
对于比较大的数据,乘法(x^2)可能会出现溢出,于是要打一个快速加来模仿乘法。
五.代码
#define m_for(i,a,b) for(int i=(a);i<=(b);++i)
#define ll long long
const int times=10;
ll multi(ll a,ll b,ll m)
{
ll ans=0%m;
while(b)
{
if(b&1)ans=(ans+a)%m,b--;
b >>= 1;
a=(a+a)%m;
}
return ans;
}
ll KSM(ll a,ll b,ll m)
{
ll ans=1%m;
while(b)
{
if(b&1)ans=multi(ans, a, m),b--;
b>>=1;
a=multi(a,a,m);
}
return ans;
}
inline bool Miller_Rabin(ll p){
if(p<=2)return p==2;
if(!(p&1))return 0;
ll u=p-1;
int power=0;
while(!(u&1))u>>=1,power++;
m_for(i,1,times){
ll a=rand()%(p-2)+2,x=KSM(a,u,p),y;
for(int i=1;i<=power;++i,x=y){
if((y=multi(x,x,p))==1&&x!=1&&x!=p-1)return 0;
}
if(x!=1)return 0;
}
return 1;
}