Miller_Rabin算法

为了测试一个大整数是不是素数,我们不能够使用传统的测试是否有因子的方法,因为那样的时间复杂度至少也是 O ( n ) O(n) O(n),空间复杂度是 O ( n ) O(n) O(n)(使用线性筛数法),时间复杂度还好说,空间复杂度在n比较大的时候完全不可以接受,这就需要我们使用 M i l l e r _ R a b i n Miller\_Rabin Miller_Rabin算法进行解决。

算法的核心使用了费马小定理和二次探测定理

费马小定理:若 p p p为质数,则 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv 1\pmod p ap11(modp)

这是一个质数的必要条件,但是有一类特殊的数也满足这个等式并且是合数(卡米歇尔数),为了剔除这些特殊的数,我们还需要二次探测定理帮助判断

二次探测定理:如果 p p p为素数, x 2 ≡ 1 ( m o d p ) x^2\equiv 1 \pmod p x21(modp),则 x = 1 或 x = p − 1 x=1 或 x=p-1 x=1x=p1

因此对于满足费马小定理的数 p p p,即 a p − 1 2 2 ≡ 1 ( m o d p ) {a^{\frac{p-1}{2}}}^2\equiv 1\pmod p a2p121(modp),我们判断 a p − 1 2 a^{\frac{p-1}{2}} a2p1是否为1或者 p − 1 p-1 p1,如果不是说明 p p p不是素数,如果是我们可以继续对 a p − 1 2 a^{\frac{p-1}{2}} a2p1进行探测

每一次检测都无法100%确定这是一个素数,但是判断8次几乎不可能出错。

这里的mult函数是乘积函数,因为接近long long 的数字相乘会爆long long,这里按位相乘。

在研究的很多人的实现后我实现了以下,觉得我这个复杂度低一些(因为自己写的缘故看起来很亲切)。实际过题时间也比之前看到的快。

typedef long long ll;
ll mult(ll x,ll y,ll p)
{
    long double d=1; d=d*x/p*y;
    return ((x*y-((ll)d)*p)%p+p)%p;
}

ll quick_pow(ll a,ll b,ll p)
{
    a%=p; ll ret=1;
    while(b)
    {
        if(b&1) ret=mult(ret,a,p);
        a=mult(a,a,p); b>>=1;
    }
    return ret;
}

bool Miller_Rabin(ll n)
{
    if(n<2) return false;
    const int times=8;
    const ll prime[times]={2,3,5,7,11,13,17,61};
    for(int i=0;i<times;i++)
    if(n==prime[i]) return true;
    else if(n%prime[i]==0) return false;
    ll x=n-1; while(~x&1) x>>=1;
    for(int i=0;i<times;i++)
    {
        ll a=prime[i]; ll now=quick_pow(a,x,n); ll last;
        if(x==n-1)
        {
            if(now!=1) return false;
        }
        else
        {
            while(x!=n-1)
            {
                x<<=1; last=now; now=mult(now,now,n);
                if(now==1)
                {
                    if(last!=1 && last!=n-1) return false;
                    break;
                }
            }
        }
    }
    return true;
}

之前看到的别人的板子

ll mult(ll a, ll b, ll p) {
    a %= p;
    b %= p;
    ll res = 0, tmp = a;
    while(b) {
        if (b & 1) {
            res += tmp;
            if (res > p) res -= p;
        }
        tmp <<= 1;
        if (tmp > p) tmp -= p;
        b >>= 1;
    }
    return res;
}

ll quick_pow(ll a, ll b, ll p) {
    ll res = 1;
    ll tmp = a % p;
    while(b) {
        if (b & 1) res = mult(res, tmp, p);
        tmp = mult(tmp, tmp, p);
        b >>= 1;
    }
    return res;
}

bool check(ll a, ll n, ll x, ll t) {
    ll res = quick_pow(a, x, n);
    ll last = res;
    for (ll i = 1; i <= t; i++) {
        res = mult(res, res, n);
        if (res == 1 && last != 1 && last != n-1) return true;
        last = res;
    }
    return res != 1;
}

bool Miller_Rabin(ll n) {
    if (n < 2) return false;
    if (n == 2) return true;
    if ((n & 1) == 0) return false;
    ll x = n-1;
    ll t = 0;
    while ((x & 1) == 0) {
        x >>= 1;
        t++;
    }

    srand(time(NULL));
    const ll tims = 8;
    for (ll i = 0; i < tims; i++) {
        ll a = rand() % (n-1) + 1;
        if (check(a, n, x, t)) return false;
    }
    return true;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值