米勒拉宾算法(素性测试)

米勒拉宾素性测试

对于一个数n,如果想要判断它是否为素数,常规的方法为试除法。即,让n依次除以2到sqrt(n)以内的整数。如果有出现除尽的情况,则为合数。
该方法的时间复杂度为O(sqrt(n))在面对n为长整型的时候有可能超出时间要求。

因此普遍采用米勒拉宾算法进行素性判定。在此之前介绍一种伪素数判定方法——小费马定理。
小费马定理为:若有素数p,则对任意的数a( a为正整数 ,且a < p), a p − 1 ≡ 1 ( m o d a^{p-1 } ≡ 1( mod ap11(mod  p ) m p )m p)m 。反之,若有任意的a( a为正整数 ,且a < p)使得p不满足 a p − 1 ≡ 1 ( m o d a^{p-1} ≡ 1( mod ap11(mod  p ) p ) p) ,p一定为合数。
可以发现若是能够举出所有的a,都能满足上式,是不是就说明p是素数呢?其实不是因为有一类合数也可以做到这一点,这一类合数叫做 Carmichael 数。前三个这样的数是561 ,1105,1729。
这样的数真让人不爽。所以采用这种方法测出来的所谓素数是不一定的。叫做伪素数。哪怕你枚举出所有的a,也不可避免。

在小费马定理的基础上有人设计出米勒拉宾随机素数测试法。可以大大的提高检测素数的正确性,但是同样并非一定正确,错误可能性却小到可以接受。

该方法同样利用了枚举多个a的做法,以提高算法的可靠性,对于每一个a,又采用了特殊的方法处理。这基于另外一个定理:如果p是素数,x是小于p的正整数,且 x 2 m o d x^2 mod x2mod  p = 1 p = 1 p=1,那么要么 x = 1 x=1 x=1,要么 x = p − 1 x=p-1 x=p1。该定理证明如下:如果p为素数,x是小于p的正整数, 且 x 2 m o d x^2 mod x2mod  p = 1 p = 1 p=1 ,说明p能够整除 ( x + 1 ) ( x − 1 ) (x+1)(x-1) x+1x1。但是p是素数,那么只可能是 x − 1 x-1 x1能被 p p p整除(此时 x = 1 x=1 x=1)或 x + 1 x+1 x+1q能被 p p p整除(此时 x = p − 1 x=p-1 x=p1)。

判断一个数是不是素数光靠上面的方法是不可靠的,因为p如果是合数的话,也有可能有 x 2 ≡ 1 m o d ( p ) x^2 ≡ 1 mod(p) x21mod(p) x = 1 x=1 x=1或者 x = p − 1 x =p-1 x=p1;但是多排除几次p不为合数的话,就增大了p是素数的可能性 ,这是这个算法的核心思想。
因此例如341这个数。可知 ( 2 340 ) ≡ 1 ( m o d ( 2^{340} ) ≡ 1 (mod (2340)1(mod  340 ) 340 ) 340); ( 2 170 ) ≡ 1 ( m o d (2^{170})≡1(mod (2170)1(mod  340 ) 340) 340); 但是发现 2 85 m o d 2^{85} mod 285mod  341 = 32 341=32 341=32。这足以证明341是一个合数,而不是一个素数。

首先判断要判断的数n是不是2,在判断n是不是奇数。然后尽可能的在令 d = n − 1 d=n-1 d=n1,在 d d d中除去2,使得 n = d ∗ ( 2 t ) n=d*(2^t) n=d(2t),d为奇数,t的值并不关心。如果n是一个素数,那么或者 a d m o d a^d mod admod  n = 1 n=1 n=1,或者存在某个i使得 a d ∗ 2 i m o d a^{d*2^i} mod ad2imod  n = n − 1 ( 0 &lt; = i &lt; r ) n=n-1(0&lt;=i&lt;r ) n=n1(0<=i<r)(注意i可以等于0,这就把 a d m o d a^d mod admod  n = n − 1 n=n-1 n=n1的情况统一到后面去了)。

a d m o d ( n ) a^d mod(n) admod(n)的算法以及求 d 2 d^2 d2的算法是采用的快速幂取模算法。但是在d为long long的情况下有可能乘法溢出。有更加优秀的算法存在。

#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;

LL mulmod( LL a, LL b , LL p )
{
    LL  d =1;
    a = a%p;
    while( b>0 )
    {
        if(b&1)
            d = (d*a)%p;
        a = (a*a)%p;
        b>>=1;
    }
    return d;
}

bool witness( LL a,LL n)
{
    LL d = n-1 ;
    if( n ==2 ) return true ;
    if( !(n&1) ) return false ;
    while(!(d&1)) d = d/2;
    LL t = mulmod(a,d,n);
    while((d!=n-1) && (t!=1)&&(t!=n-1))
    {
        t = mulmod( t ,2,n);
        d=d<<1;
    }
    return (t==n-1)||(d&1);
}

bool isprime( LL n)
{
    int a[3] = {2,7,61};
    for(int i=0;i<3;i++)
        if(!witness(a[i],n))
            return false;
    return true;
}
int main()
{
    LL s;
    cin>>s;
    if(isprime(s))
        cout<<"YES";
    else
        cout<<"NO";
    return 0;
}

转自:https://blog.csdn.net/qq_37957829/article/details/77335072

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值