Miller_Rabin素数判定

参考的一篇很棒的文章         参考的另一篇很棒的文章


Miller_Rabin素数判定

对于素数判定,首先知道最朴素的方法——试除法。从2到sqrt(N),复杂度O (sqrt(N))当然,如果把素数预处理出来,复杂度要优秀一些。由素数分布定理:对正实数x,定义π(x)为素数计数函数,即不大于x的素数个数,则有:\small \pi(x)\approx \frac{x}{lnx}

处理出素数以后,只试除2到sqrt(N)之间的素数,复杂度约为\small O(\frac{\sqrt{N}}{ln(\sqrt{N})})。然而对于较大的数还是无能为力,比如2333333333333333333。那么有一个更优秀的算法:Miller_Rabin算法。

首先要知道费马小定理:

已知n为素数,且a满足\small gcd(a,n)=1

那么\small a^{n-1}\equiv1 \ (mod\ n),证明可以看一看神仙的博客,这里就不赘述了。

然而反过来不一定。比如著名的伪素数341,它满足\small 2^{341-1}\equiv1\ (mod\ 341),然而是一个合数,341=11*31。

1903年,马洛(Malo)证明:若n为伪素数,则

也是一个伪素数,从而肯定了伪素数的个数是无穷的。

 

等等,如果把底数换成除2以外和n互质的数呢?

呃呃,很可惜,还存在一种Carmichael数,对于所有正整数b,\small gcd(b,n)=1,都有同余式\small b^{n-1}\equiv1 \ (mod\ n)成立。比如561。

 

好吧,先看一个东西:二次探测——

对于素数p,满足  \small x^{2}\equiv1\ (mod\ p)  的同余类有且只有两个,\small x\equiv1\ (mod\ p)   和   \small x\equiv p-1\ (mod\ p)

通俗地讲,就是如果\small x^{2}\equiv1\ (mod\ p),那么x除以p,余数只可能是1或p-1。

如果x%p!=1或p-1且\small x^{2}\equiv1\ (mod\ p),那么x就一定是合数了。

证明也在神仙的博客,这里不赘述了。

 

看一下算法的大致流程:

如果是小一点的数,直接先用欧拉筛法预处理。

 

inline void linear_sieves(int n=maxn-10){
    mark[1]=1;
    for(int i=2;i<=n;++i){
        if(!mark[i]) P[++cnt]=i;
        for(int j=1;((j<=cnt)&&(i*P[j]<=n));++j){
            mark[i*P[j]]=1;
            if(!i%P[j]) break;
        }
    }
}

对于没有筛到的大数,令其为n:

1.首先判断n是否为几个小素数的倍数。令这几个素数为P[1]~P[10](就测前10个吧),因为要保证n与这个素数互质(费马小定理)。如果n是它们的倍数,直接返回false。

2.否则分解n-1.令n-1=2^{q}*m,其中m为奇数。通俗地讲,就是把n-1中的2都提出来,求出q和m。

3.分别以P[1]~P[10]作为底数,利用费马小定理和二次探测来检验——首先,假设当前的底数为a。

4.如果有a^{2^{i}*m}\equiv1\ (mod\ n),那么对于一个质数,一定满足a^{2^{i-1}*m}\equiv1\ or\ (n-1)\ (mod\ n)(二次探测)。如果不满足的话,一定是合数,直接return false。于是我们可以先算出a^m,然后不断地平方,检验。

5.计算到最后,如果a^{2^q*m}\equiv1\ (mod\ n),即a^{n-1}\equiv1\ (mod\ n),才能说明是极大概率是质数。因为费马小定理是成为质数的必要非充分条件,就是说不满足费马小定理的一定是合数。通俗地讲,经历千辛万苦算到这一步,要是a^{n-1}\not\equiv1\ (mod\ n),还是只能安安静静地做一个合数。

5.如果这几个底数全部满足二次探测和费马小定理,那么恭喜这个数,极大极大的概率是一个质数。

复杂度的话,用龟速乘的上限是O(k*log^2n),其中k为测试的轮数,就是说用了几个素数为底数测试。

然而用神奇的O(1)快速乘就变成了O(k*log\ n)

该部分代码如下:

inline ll mul(ll a,ll b,ll mod){return (a*b-(ll)((long double)a/mod*b)*mod+mod)%mod;}
inline ll quick_pow(ll a,ll b,ll mod,ll ret=1){
    for(;b;b>>=1,a=mul(a,a,mod))
    if(b&1) ret=mul(ret,a,mod);
    return ret;
}
//P数组是用欧拉筛筛出的素数
//这里p代表作为底数的素数,n=(2^q)*m
inline bool check(int p,int q,ll m,ll n){
    ll num=quick_pow(p,m,n),pre=num;
    while(q--){
        num=mul(num,num,n);
        if(num==1&&pre!=1&&pre!=n-1) return false;
        pre=num;
    }return num==1;
}
inline bool isprime(ll x){
    if(x<=maxn-10) return !mark[x];
    for(int i=1;i<=10;++i) if(!x%P[i]) return false;
    int q=0;ll m=x-1;while(!(m&1)) m>>=1,q++;
    for(int i=1;i<=10;++i) if(!check(P[i],q,m,x)) return false;
    return true;
}

 

例题:HDU2138

代码:

#include
#define ll long long
using namespace std;
const int maxn=1e5+10;
bitset mark;
int P[maxn],cnt,T,n,ans;
inline ll mul(ll a,ll b,ll mod){return (a*b-(ll)((long double)a/mod*b)*mod+mod)%mod;}
inline ll quick_pow(ll a,ll b,ll mod,ll ret=1){for(;b;b>>=1,a=mul(a,a,mod)) if(b&1) ret=mul(ret,a,mod);return ret;}
inline void linear_sieves(int n=maxn-10){
    mark[1]=1;
    for(int i=2;i<=n;++i){
        if(!mark[i]) P[++cnt]=i;
        for(int j=1;((j<=cnt)&&(i*P[j]<=n));++j){
            mark[i*P[j]]=1;
            if(!i%P[j]) break;
        }
    }
}
inline bool check(int p,int q,ll m,ll n){
    ll num=quick_pow(p,m,n),pre=num;
    while(q--){
        num=mul(num,num,n);
        if(num==1&&pre!=1&&pre!=n-1) return false;
        pre=num;
    }return num==1;
}
inline bool isprime(ll x){
    if(x<=maxn-10) return !mark[x];
    for(int i=1;i<=10;++i) if(!x%P[i]) return false;
//(!(m&1))表示m为2的倍数。因为这表示m的二进制的最后一位为0。
    int q=0;ll m=x-1;while(!(m&1)) m>>=1,q++;
    for(int i=1;i<=10;++i) if(!check(P[i],q,m,x)) return false;
    return true;
}
inline int read(){
    int x=0;char ch=getchar();
    while(!isdigit(ch)) ch=getchar();
    while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x;
}
inline void print(int x){
    if(x>9) print(x/10);
    putchar(x%10+'0');
}
int main(){
    linear_sieves();
    while(~scanf("%d",&n)){
        ans=0;
        while(n--) ans+=isprime(read());
        print(ans),putchar(10);
    }
}

 

 

 

 

 

你可能感兴趣的:(Miller_Rabin素数判定)