【随机算法】Miller-Rabin大素数检测算法(蒙特卡罗方法)

费马小定理

  假如p是质数,且(a,p)=1,那么 a^(p-1)≡1(mod p)。

  即:假如a是整数,p是质数,且a,p互质(即两者只有一个公约数1),那么a的(p-1)次方除以p的余数恒等于1。

算法1

  事实上,质数一定满足以上条件,但是满足以上条件的不一定是质数。

  但是我们还是先利用这个性质来判断一个数N是否为素数。首先我们看一下算法1:

bool prime(int n)
{
    随机选择一个整数a,满足a<n;
    if (a^(n-1)≡1mod n))
        return true;
    else
        return false;
}

  但是这个算法可靠吗?比如341就不是素数,如果选到a=2,则会误判。

  Lemma: If a^(N-1) != 1 (mod N) for some a relatively prime to N, then it must hold for at least half the choices of a < N.

  这一引理说明,算法一正确的概率不低于1/2。

算法2

  既然取一个a进行测试的误判概率不高于1/2,则我们可以取k个a进行测试,则误判概率不高于1/(2^k)。

bool prime(int n)
{
    随机选择一组整数ai,满足ai<n;
    if (对所有的ai,都有ai^(n-1)≡1mod n))
        return true;
    else
        return false;
}

  合适地选取k,出错的概率已经很小了,但是还是有问题:

  当遇到卡米克尔数时,算法二出错的概率依然高(除非选到的a刚好和N不互素,否则一定会错判为素数)

  定义:如果N是合数,且对于所有和N互素的整数a,都有 a^(N-1) ≡1 (mod N),则称N是一个卡米克尔数。

算法3

二次探测定理:对于素数p,0<x<p,x^2 mod p =1 => x=1或p-1。

  由上述定理可推导出:
  
  如果n是一个奇素数,将n−1表示成m*2^j的形式,m是奇数,a与n是互素的任何整数,那么a^m≡1 mod n或者对某个i(0 ≤i≤ j−1, i∈Z)等式a^(2im)≡−1 mod n成立。

Miller-Rabin算法的一般步骤:

0、先计算出m、j,使得n-1=m*2^j,其中m是正奇数,j是非负整数

1、随机取一个a,2<=a

2、计算v=a^m mod n

3、如果v==1,通过测试,返回

4、令i=1

5、如果v=n-1,通过测试,返回

6、如果i==j,非素数,结束

7、v=v^2 mod n,i=i+1

8、循环到5

Miller-Rabin算法的误判概率

  经过独立的t轮Miller-Rabin算法将一个合数误判为素数的可能性不大于4^(−t)。

C++实现

#include <iostream> 

using namespace std; 

typedef unsigned __int64 llong; 
 //求(x*y)%n
llong mod_pro(llong x,llong y,llong n) 
{ 
    llong ret=0,tmp=x%n; 
    while(y) 
    { 
        if(y&0x1)if((ret+=tmp)>n)ret-=n; 
        if((tmp<<=1)>n)tmp-=n; 
        y>>=1; 
    } 
    return ret; 
} 
 //快速幂,求(a^b) % c
llong mod(llong a,llong b,llong c) 
{ 
    llong ret=1; 
    while(b) 
    { 
        if(b&0x1)ret=mod_pro(ret,a,c); 
        a=mod_pro(a,a,c); 
        b>>=1; 
    } 
    return ret; 
} 

llong ran() 
{ 
    llong ret=rand(); 
    return ret*rand(); 
} 
 //参数t为测试的轮数
bool is_prime(llong n,int t) 
{ 
    if(n<2)return false; 
    if(n==2)return true; 
    if(!(n&0x1))return false; 
    llong k=0,m,a,i; 
    for(m=n-1;!(m&1);m>>=1,k++); 
    while(t--) 
    { 
        a=mod(ran()%(n-2)+2,m,n); 
        if(a!=1) 
        { 
            for(i=0;i<k&&a!=n-1;i++) 
                a=mod_pro(a,a,n); 

            if(i>=k)return false; 
        } 
    } 
    return true; 
} 

int main() 
{ 
    llong n; 
    while(scanf("%I64u",&n)!=EOF) 
        if(is_prime(n,3)) 
            cout<<"YES\n"; 
        else 
            cout<<"NO\n"; 
        return 0; 
}

你可能感兴趣的:(算法,蒙特卡罗,素数检测,Rabin,Miller)