[玄学算法 Miller_Rabin 素数测试]

今天做 dy0607 d y 0607 的模拟题有一道暴力分可以打 Miller_Rabin M i l l e r _ R a b i n
正好之前还没有看懂这个鬼算法,数学一本通上也没讲清
所以下午还是去学了一下 不过中午不睡觉下午效率真低,学了半天才过模板

首先引入两个定理

费马小定理:

如果 a a , p p 互质, 则有 ap1=1(mod p) a p − 1 = 1 ( m o d   p )

二次探测定理

a2=1(mod p) a 2 = 1 ( m o d   p ) 如果 p p 为质数 则 a=1(mod p) a = 1 ( m o d   p ) a=p1(mod p) a = p − 1 ( m o d   p )

定理的证明在此处就不给出了 主要讲下算法流程

Miller_Rabin

1.首先选择几个已知的小质数, 将其倍数筛去
2.选定进行 n n 次测试,通过 n n 次测试不是质数的概率为 (1/4)n ( 1 / 4 ) n
3.首先把要测试的数 x x 化成 x=2cntt+ x = 2 c n t t + 1 ( t t 为奇数)的形式 再随机一个数 a a 满足 0<a<x 0 < a < x ,快速幂计算 tmp=at t m p = a t , 然后对 tmp t m p 进行 cnt c n t 次二次探测定理进行测试,如果所有测试都通过且最后 tmp=1 t m p = 1 的话就通过了一次测试,这样不断循环直到结束即可,一般来说 n n 不用选太大, 510 5 ~ 10 次就可以了。

洛谷模板 线性筛质数

Codes

#include
#define For(i, a, b) for(register int i = a; i <= b; ++ i)

using namespace std;
typedef long long ll;

ll qpow(ll a, ll x, ll mod) {
    ll ret = 1;
    for(; x; x >>= 1, (a *= a) %= mod)
        if(x & 1) 
            (ret *= a) %= mod;
    return ret;
}

bool Miller_Rabin(int mod) {
    if(mod == 2 || mod == 7 || mod == 61) return true;
    if(mod > 1 && (mod % 2) && (mod % 7) && (mod % 61)) {
        For(j, 1, 5) {
            int x = mod - 1, tot = 0;
            while(x % 2 == 0)
                x >>= 1, ++ tot;
            int a = rand() % (mod - 1) + 1;
            x = qpow(a, x, mod);
            For(i, 1, tot) {
                int y = 1ll * x * x % mod;
                if(y == 1 && x != 1 && x != mod - 1)
                    return false;
                x = y;
            }
            if(x != 1) return false;
        }
        return true;
    }
    return false;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("3383.in", "r", stdin);
    freopen("3383.out", "w", stdout);
#endif
    int n, q, x;
    scanf("%d%d", &n, &q);
    For(i, 1, q){
        scanf("%d", &x);
        if(Miller_Rabin(x)) 
            puts("Yes");
        else 
            puts("No");
    }
    return 0;
}

你可能感兴趣的:(数学-数论相关)