Miller-Rabin
作者:互联网
算法简介
Miller-Rabin 算法可以较快地判断一个数是不是质数。
但是,这个算法是概率性算法,即有一定的概率会误判。
所以,一般会使用较多的数检验,从而降低误判的概率。
前置定理
费马小定理:\(a^{p-1} \equiv 1 (mod\ p)\)
注意:费马小定理的逆命题并不一定成立。
二次探测定理:若 \(p\) 为奇素数且 \(x^2 \equiv 1 (mod\ p)\) , 则 \(x \equiv 1 (mod\ p)\) 或 \(x \equiv p-1 (mod\ p)\)
前置算法
快速乘,快速幂
判断过程
设现在用于判断的质数为 \(p\) , 在判断的数为 \(x\) 。
首先,若 \(x=p\) ,则 \(x\) 为质数;若 \(x\) 是 \(p\) 的倍数,则 \(x\) 为合数。
然后,根据费马小定理,若 \(p^{x-1} \% x \neq 1\) , 则 \(x\) 必然是合数。
因为费马小定理的逆命题并不一定成立,所以,现在还并不能确定 \(x\) 就是质数。
接下来,令 \(k=x-1\) 。
1.令 \(k=\frac{k}{2}\) , 设 \(t=p^k \% x\) 。
2.若 \(t \neq 1\) 且 \(t \neq p-1\) ,又因为 \(p^{2k} \% x=1\) ,根据二次探测定理, \(x\) 为合数。
3.若 \(t=p-1\) 或 \(k\) 是奇数,无法继续判断,所以判断 \(x\) 为质数。否则,返回第一步,继续判断。
将多个质数的判断结果合并在一起,即可得到一个正确率极高的结果。
code
#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
const int P[]={2,3,5,7,11,13,17,19,23,29};
int MUL(int a,int b,int p) //a*b%p
{
if(!b) return 0;
if(b==1) return a;
int sum=MUL(a,b/2,p);
if(b%2) return ((sum+sum)%p+a)%p;
return (sum+sum)%p;
}
int POW(int a,int b,int p) //a^b%p
{
if(!b) return 1;
if(b==1) return a;
int sum=POW(a,b/2,p);
if(b%2) return MUL(MUL(sum,sum,p),a,p);
return MUL(sum,sum,p);
}
bool check(int x)
{
if(x==0||x==1) return false;
if(x==2) return true;
for(int i=0;i<10&&P[i]<=x;i++)
{
if(x==P[i]) return true;
if(x%P[i]==0) return false;
if(POW(P[i],x-1,x)!=1) return false;
int k=x-1,t=0;
if(k%2) continue;
k/=2,t=POW(P[i],k,x);
if(t==x-1||k%2) continue;
while(1)
{
k/=2,t=POW(P[i],k,x);
if(t!=1&&t!=x-1) return false;
if(t==x-1||k%2) break;
}
}
return true;
}
int x;
signed main()
{
scanf("%lld",&x);
puts(check(x)?"YES":"NO");
return 0;
}
一些提醒
此算法的时间复杂度为 \(O(k \log^3 n)\) (其中, \(k\) 为用于判断的质数个数),在数据较大时非常优秀。
但是,在数据比较小的时候此算法的效率和 \(O(\sqrt{n})\) 的暴力都差很多。所以,在数据小时要慎用。
同时,在乘法不会爆 \(long\ long\) 时最好把快速乘去掉,可以把复杂度降为 \(O(k \log^2 n)\) 。
标签:return,int,Miller,sum,算法,MUL,质数,Rabin 来源: https://www.cnblogs.com/zhs1/p/14043999.html