Miller Rabin 素数测试
Miller Rabin 是一种快速判断素数的方法。有非常小的概率可能出错。
费马小定理和二次探测
Miller Rabin 素数测试主要就是用到了以上两个理论。
费马小定理: 如果 p p p为质数,那么有 a p − 1 ≡ 1 m o d p a^{p-1} ≡1~~mod~~ p ap−1≡1 mod p
二次探测: 对于质数 p p p,如果 a 2 ≡ 1 m o d p a^2 ≡ 1~~mod~~p a2≡1 mod p,那么 a ≡ 1 a ≡ 1 a≡1或 a ≡ p − 1 a ≡ p-1 a≡p−1
证明请自行百度。
判断 p p p是否为质数
我们利用以上两个理论来进行素数判断
-
如果 p p p 为偶数且不为 2 2 2,我们可以直接判断出来
-
将 p − 1 p-1 p−1分解成 2 k × t 2^k\times t 2k×t的形式,记录下 k , t k,t k,t
-
对于一个数 a a a,我们首先求出 a t a^t at,然后进行二次探测,每次将 a a a 平方,二次探测 k k k次
-
如果以上全通过且满足费马小定理,那么我们判断 p p p为质数
-
只选一个 a a a有很大概率出错,所以一般多选几个(竞赛用2,3,5,7,11,13,17,19,23即可),且尽量选质数
注意:可能会爆long long
,所以要采用快速乘。
期望时间复杂度: O ( n 1 4 ) O(n^{\frac{1}{4}}) O(n41)
C o d e \mathcal{Code} Code
/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年11月06日 星期三 19时24分57秒
*******************************/
#include<cstdio>
#include<algorithm>
using namespace std;
struct IO{
template<typename T>
IO & operator>>(T&res)
{
T q=1;char ch;
while((ch=getchar())<'0' or ch>'9')if(ch=='-')q=-q;
res=(ch^48);
while((ch=getchar())>='0' and ch<='9') res=(res<<1)+(res<<3)+(ch^48);
res*=q;
return *this;
}
}cin;
const int primes[]={2,3,5,7,11,13,17,19,23};
const int SIZE=9;
long long mul(long long a,long long b,long long mod)
{
long long ans=0;
while(b)
{
if(b&1)
ans=(ans+a)%mod;
a=(a+a)%mod;
b>>=1;
}
return ans;
}
long long ksm(long long a,long long b,long long mod)
{
long long res=1;
while(b)
{
if(b&1)
res=mul(res,a,mod);
a=mul(a,a,mod);
b>>=1;
}
return res;
}
bool Miller_Rabin(long long x)
{
if(!(x&1))return false;
long long tmp=x-1,s=0,k,nxt;
while(!(tmp&1))s++,tmp>>=1;
for(int i=0;i<SIZE;i++)
{
if(x==primes[i]) return true;
k=ksm(primes[i],tmp,x);
for(int j=1;j<=s;j++)
{
nxt=mul(k,k,x);
if(nxt==1 && k!=1 && k!=x-1)
return false;
k=nxt;
}
if(k!=1) return false;
}
return true;
}