算法竞赛模板 素数测试(Miller-Rabin测试)

基础素数测试模板

对于大数的素性判断,目前Miller-Rabin算法应用最广泛。一般底数仍然是随机选取,但当待测数不太大时,选择测试底数就有一些技巧了。比如,如果 被测数小于4759123141,那么只需要测试三个底数 a[]={2,7,61} 就足够了。当然,测试的越多,正确的范围也越大。如果你每次都用前7个素数 a[]={2,3,5,7,11,13,17} 进行测试,所有不超过341550071728320的数都是正确的。如果选用 a[]={2,3,7,61,24251} 作为底数,那么10^16内唯一的强伪素数为46856248255981。这样的一些结论使得Miller-Rabin算法在OI中非常实用。通常认为,Miller-Rabin素性测试的正确率可以令人接受,随机选取k个底数进行测试算法的失误率大概为4^(-k)。

tip:1无法进行判断,只能自行特判为false!

#include<iostream>
using namespace std ;
typedef long long ll;
ll pow_mod(ll a,ll b,ll r)
{
    ll ans=1,buff=a;
    while(b)
    {
        if(b&1)
            ans=(ans*buff)%r;
        buff=(buff*buff)%r;
        b>>=1;
    }
    return ans;
}

bool test(ll n,ll a,ll d)
{
    if(n==2)return true;
    if(n==a)return false;
    if(!(n&1))return false;
    while(!(d&1))d>>=1;
    ll t=pow_mod(a,d,n);
    while(d!=n-1&&t!=n-1&&t!=1)
    {
        t=t*t%n;
        d<<=1;
    }
    return t==n-1||(d&1)==1;//要么t能变成n-1,要么一开始就t=1
}

bool isprime(ll n)
{
    int a[]={2,3,5,7};      //看情况取值
    for(int i=0;i<=3;i++)
    {
        if(n==a[i])return true;
        if(!test(n,a[i],n-1))return false;
    }
    return true;
}
int main()
{ 
    int t;
    ll n;  
    for(cin>>t;t;t--)
    {
        cin>>n;
        cout<<((isprime(n))?"Yes":"No")<<endl;
    }
    return 0; 
}

ps:注意上述算法中的幂运算是longlong类型,longlong×longlong肯定会出现溢出现象,如果不会java大整数,手里也没有大整数乘法模板的话,有一个小技巧可以避免溢出,方法就是乘法改为加法,把上面的代码:

ll pow_mod(ll a,ll b,ll r)
{
    ll ans=1,buff=a;
    while(b)
    {
        if(b&1)
            ans=(ans*buff)%r;
        buff=(buff*buff)%r;
        b>>=1;
    }
    return ans;
}

改为:

ll mod_mul(ll a,ll b,ll n)
{
    ll res=0;
    while(b)
    {
        if(b&1)
            res=(res+a)%n;
        a=(a+a)%n;
        b>>=1;
    }
    return res;
}

ll pow_mod(ll a,ll b,ll n)
{
    ll res=1;
    while(b)
    {
        if(b&1)
            res=mod_mul(res,a,n);
        a=mod_mul(a,a,n);
        b>>=1;
    }
    return res;
}

 

posted @ 2018-09-01 10:56  真想不出名字了  阅读(614)  评论(0编辑  收藏  举报