typedef long long LL;
Miller_Rabin素数测试
二次探测定理
如果
p是素数,
x是小于
p的正整数,且
x2≡1(modp),那么要么
x=1,要么
x=p−1。
解读: 因为
x2modp=1,即
p∣x2−1,也即
p∣(x+1)(x−1)。
由于
p是素数且
x<p,那么只可能是
x−1能被
p整除(此时
x=1)或
x+1能被
p整除(此时
x=p−1)。
要测试整数
n是否为素数,若
n是素数,根据费马定理,有
an−1modn=1。
首先任选一个数做底数
a。
我们令
n−1=d⋅2R,其中
d是一个奇数,于是
an−1modn=(ad)2Rmodn=ad⋅2R−1⋅ad⋅2R−1modn
根据二次探测定理,
ad⋅2R−1modn要么等于
1要么等于
n−1。
如果
ad⋅2R−1modn既不等于
1也不等于
n−1,
n一定不是素数,结束讨论。
如果
ad⋅2R−1modn=n−1,我们认为
n是素数,结束讨论。
如果
ad⋅2R−1modn=1,继续讨论:
- 根据二次探测定理,
ad⋅2R−2modn要么等于
1要么等于
n−1。……
上述讨论过程中,如果存在某个整数
k,使得
ad⋅2kmodn=n−1(0⩽k<R),我们就认为
n是素数,结束讨论。
或者,若
admodn=1,
n是素数。
这是一种不确定算法,每一次测试的准确率约为
75%。
若进行了
m次测试,时间复杂度为
O(mlog3n)。
bool miller_rabin(LL n){
if(n==2)return 1;
if(n<2||!(n%2))return 0;
LL d=n-1;int r=0;
while(!(d&1))d/=2,r++;
for(int i=1;i<=10;i++){
LL a=rand()%(n-2)+2,x=Pow(a,d,n);
for(int j=1;j<=r;j++){
LL y=Mul(x,x,n);
if(y==1&&x!=1&&x!=n-1)return 0;
x=y;
}
if(x!=1)return 0;
}
return 1;
}
一些玄学操作:
-
n<4759123141时,只需检测
a=2,7,61,即可确保正确。
-
n<2152302898747时,只需检测
a=2,3,5,7,11,即可确保正确。
-
n<341550071728320时,只需检测
a=2,3,5,7,11,13,17,即可确保正确。
线性求逆元
a−1={1(p−⌊ap⌋)(pmoda)−1modpa=1a>1
证明:
显然
1的逆元为
1。
a>1时,已知
a⋅a−1≡1(modp),其中
1<a<p。
令
k=⌊ap⌋,r=pmoda,那么
p=ka+r,0<r<a
则有
ka+r≡0(modp)
两边同时乘以
a−1r−1得到
(ka+r)a−1r−1≡0(modp)
即
k⋅r−1+a−1≡0(modp)
转换得
a−1=−k⋅r−1modp=−⌊ap⌋(pmoda)−1modp=(p−⌊ap⌋)(pmoda)−1modp
时间复杂度:
O(n)
void make_inv(int*inv,int n,int p){
inv[1]=1;
for(int i=2;i<=n;i++)inv[i]=(p-p/i)*inv[p%i]%p;
}