zoukankan      html  css  js  c++  java
  • Miller-Rabin素性测试

    可以去看本文的大部分来源:数论部分第一节:素数与素性测试

    在判断一个数是否为质数时,我们通常选择根号 n 的试除法来进行判断

    但这是在询问比较少/数据比较小的前提下的

    Miller-Rabin 素性测试为我们提供了一种 logn 的做法

    我们知道 Fermat 小定理 a^(p - 1) mod p = 1 是对于 p 为质数且 a,p 互质的情况成立的

    那么其逆命题成立吗?

    过去人们是这样认为的

    但由于伪素数的发现,人们终于认识到它是错误的

    不过这样的数还是占据少数的

    所以我们至少可以用它来先验证出一部分数,尽管这个集合中有伪素数

    可以通过多取一些 a 的值来降低错误概率

    以上是Fermat素性测试。

    那么我们自然会这样想,如果我们考虑了所有的小于 n 的底数 a ,是否就足够区分素数和其他数呢?

    很可惜,这样的合数依然存在

    他们被称为“Carmichael数

    你一定会认为这样极端的伪素数一定很大

    然而第一个 Carmichael数仅仅是一个三位数:561

    而且前 10 亿个自然数中 Carmichael数有600个之多

    Carmichael数的存在说明,我们还需要继续加强素性判断的算法。

    Miller和Rabin两个人的工作让Fermat素性测试迈出了革命性的一步,建立了传说中的Miller-Rabin素性测试算法

    新的测试基于下面的定理:

      如果 p 是素数,x 是小于 p 的整数,且 x^2 mod p = 1 ,那么要么 x = 1 ,要么 x = p - 1,这是显然的,因为 x^2 mod p = 1 相当于 p 能整除 x^2 - 1,也即p能整除 (x + 1)(x - 1) 。由于 p 是素数,那么只可能是 x - 1 = 0(mod p) (此时 x = 1 )或 x + 1 = 0(mod p) 整除(此时 x = p - 1 )。

    下面用 341 来演示一下上面的定理如何运用到 Fermat定理上:

       2^340 mod 341 = 1  => (2^170)^2 mod 341 = 1

      若 341 为素数,则上面方程的解可能是 1 或 340,若等于 1,我们继续上面的变形得:

      (2^85)^2 mod 341 = 1

      然而计算得 2^85 mod 341 = 32

      那么 341 不是素数

    这就是 Miller-Rabin 素性测试的方法,通过不断提取指数 p - 1中的因子 2 ,将方程化为 x^2 mod p = 1 的形式, 根据解 只能为 1 或 p - 1 ,若解为 1,继续重复上述过程,解为 p - 1,停止,这个数目前为止我们认为它可能是一个素数,解为其他数,则这个数不是素数

    时间复杂度 O(logn)

    Miller-Rabin素性测试同样为不确定算法,我们把可以通过以a为底的Miller-Rabin测试的合数称作以a为底的强伪素数(strong pseudoprime)。第一个以2为底的强伪素数为2047。第一个以2和3为底的强伪素数则大到1 373 653。

    以上是 Miller-Rabin 素性测试算法。

    通常我们使用该算法时,都是要来判断 long long 级别的数字,由于可能在快速幂里乘法的步骤乘爆了,需要快速乘, O(logn),写起来跟快速幂差不多

    附上一道例题:hihocoder 1287 数论一·Miller-Rabin质数测试

    代码:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cctype>
    #include<cstdio>
    using namespace std;
     
    typedef long long ll;
     
    int m, n;
    ll ps[12] = {2ll, 3ll, 5ll, 7ll, 11ll, 13ll, 17ll, 19ll, 23ll, 29ll, 31ll, 37ll};
     
    inline ll turtle_mul(ll a, ll b, ll mod) {
        ll ans = 0ll;
        while(b) {
            if(b & 1) ans = (ans + a) % mod;
            a = (a << 1) % mod;
            b >>= 1;
        }
        return ans;
    }
    inline ll fast_pow(ll low, ll up, ll mod) {
        ll ans = 1ll;
        low %= mod;
        while(up) {
            if(up & 1ll) ans = turtle_mul(ans, low, mod);
            up >>= 1;
            low = turtle_mul(low, low, mod);
        }
        return ans;
    }
    inline bool dvd_chk(ll bot, ll top, ll mod) {
    	register ll tmp;
    	while (!(top & 1)) {
    		tmp = fast_pow(bot, top, mod);
    		if (tmp == 1ll) top >>= 1;
    		else if (tmp == mod - 1ll) return true;
    		else return false;
    	}
    	return true;
    }
    inline bool Test(ll a) {
        if(a <= 1) return false;
        if(a == 2ll) return true;
        if(!(a & 1ll)) return false;
        for(int i = 0; i < 12; ++i) {
            if(ps[i] == a)
                return true;   
            if(fast_pow(ps[i], a - 1ll, a) != 1ll)
                return false;  
            else if(!dvd_chk(ps[i], a - 1ll, a))
                return false;
        }
        return true;
    }
     
    int main() {
        scanf("%d", &m);
        ll num;
        while(m--) {
            scanf("%lld", &num);
            if(Test(num)) puts("Yes");
            else puts("No");
        }
        return 0;
    }
    

      

    禁止诸如开发者知识库/布布扣/码迷/学步园/马开东等 copy 他人博文乃至博客的网站转载 ,用户转载请注明出处:https://www.cnblogs.com/xcysblog/
  • 相关阅读:
    在安装了Anaconda+Pycharm怎么导入OpenCV
    JS--ECMAScript
    JS--DOM
    JS-BOM
    浮动 高度塌陷
    CSS2--字体样式
    CSS2--文本样式
    css2--垂直对齐
    css2--背景
    HTML基础
  • 原文地址:https://www.cnblogs.com/xcysblog/p/9261949.html
Copyright © 2011-2022 走看看