0.1 一些闲话

最近一次更新是在2019年11月12日。之前的文章有很多问题:当我把我的代码交到LOJ上,发现只有60多分。我调了一个晚上,尝试用{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 61, 24251, 2147483647, 998244353}这么一大串数作为基底,然后左改右改,总算过去了。特别感谢 @骗分过样例 的提醒,现在张贴的代码应该是值得信赖的了。

之前我的同学好像就指出过我的文章的很多问题。比如说我之前写到,Miller Rabin在面对合数\(46856248255981\)时,如果用{2,3,7,61,24251}做基底,是会判断错误的。但实际上,他说他对着我的代码写了一遍,发现这个数是可以判掉的。

OI中的数学需要细心。相比其他算法方面,数学真的不好调试——一个公式算错了,一个下标写反了,程序就错了。而且复杂的数学代码很难用gdb查错,只能反复自己的公式是否写对,并且在转换成代码的时候是否有差错。编程需要细致和求实精神。当你在写代码,亦或是在写题解时,多问自己一个:有没有问题?是不是哪里写错了?这里为什么要这么写?可不可以造数据Hack?尤其是在写博客的时刻,每一个OIer都需要做到足够细致——因为这些文章不是写出来好看,让同学膜拜的,而是真的要帮到网络另一端,需要帮助的人。

这是我尤为欠缺的。最近我的博文被指出了很多错误,我也意识到自己的不细致会给自己带来这么多麻烦。也希望我能作为反面教材,警醒后人吧。

1.1 问题引入

判断一个数\(n\)是不是质数.

当n不太大时,我们直接用\(O(\sqrt{n})\)的试除法就可以了.听说还可以利用素数定理,只用\(O(ln(x))\)就可以了?这种说法我还暂时没试过.

不过,对于\(n \geqslant 10^{18}\)时,这个算法是肯定不可取的.我们需要一个更加快速的算法.谁不希望所有的问题被\(O(1)\)解决呢?

一想到\(O(1)\),我们不妨试一下随机算法.在之后介绍Pollard Rho算法后,也会用到这种思想.

1.2 随机的依据

按理来讲,我们应该是在\([1,\sqrt{n}]\)里面随机,再通过某些方式来提高正确率的.但是这样做太慢!我们要确保每一个在\([1,\sqrt{n}]\)中的数都不是n的因子,显然用随机试的算法是行不通的.那么,我们该怎么办?

数论中,我们有Fermat(也就是费马)小定理:

$ p \in \text{Prime},\quad x^p \equiv p \pmod{p}$ 或 $ x^{p-1} \equiv 1 \pmod{p}$.

这个定理对于任何质数成立;但是反过来不一定.满足Fermat小定理的数不一定是质数.事实上,如果对任意$ b \in \mathbb{N}, b^{p-1} \equiv 1\pmod{p}$,我们就称p为Carmichael数.运用Fermat小定理,我们可以判定一个数不是质数,但是不能判定一个数是质数.

不过,这给我们的随机枚举提供了一个不错的启示:不是随机枚举\(n\)的因子,而是一个基底\(b\).若对于不同的基底,都有\(b^{p-1} \equiv 1 \pmod{p}\),p是质数的可能性就更大了.事实上,如果p满足\(b^{p-1} \equiv 1 \pmod{p}\),那么p就叫做"基于b的伪素数".

如果可能的话,这里会张贴费马小定理的证明.

1.3 算法的改进: 二次探测定理

这个定理叫做"二次"是有原因的.其一,它是费马小定理探测质数的辅助工具,作为第二道屏障;其二,它和一个"二次同余方程"有关.定理如下:

若\(x^2 \equiv 1 \pmod{p}\),则 \(x \equiv 1 \pmod{p}\) 或 \(x \equiv p-1 \pmod{p}\)

这个定理比较好证明.这里是证明过程.

对于一个质数p,只要p和二次探测定理不符,那么我们就可以肯定p不是质数.事实上,我们常常直接用这个定理去判定质数,而不是用费马小定理.

2.1 基本实现

无论是费马小定理还是二次探测定理,我们都需要选取若干个合适的基底来进行测试.一般而言,我们会选取这五个基底:\(2,3,7,61,24251\)。当需要测试的数据规模在\(10^{16}\)左右时,它的表现效果还是可以的。但当数据规模更大些时,你必须考虑将基底扩大一些。在这个基底的基础上,可以考虑选取前\(10\sim 15\)大的质数,从而解决几乎\(100\%\)的数据。

对于每一个基底\(b\)和一个待判断的数\(x\),我们进行如下测试:
1.用费马小定理判定一下这个数是不是合数.
2.如果\(x-1\)是偶数,我们可以对费马小定理做如下变形:

\(b^{x-1}\equiv 1 \pmod{x} \implies b^{\frac{x-1}{2}\cdot 2} \equiv 1 \pmod{x}\)
这样就可以用二次探测定理看一下有没有\(b^{\frac{x-1}{2}} \equiv 1 \text{或} x-1 \pmod{x}\) 了.如果都不满足,x就一定不是质数.

如果\(\frac{x-1}{2}\)同样是一个偶数,我们可以继续将它拆成\(\frac{x-1}{4} \cdot 2\),然后再用一次二次探测定理来做.

3.对于二次探测定理\(X^k \equiv 1 \pmod{x}\),如果\(k\)是奇数或者\(X \equiv x-1 \pmod{x}\),此时我们没有办法再拿这个数做文章了.我们只能暂时认定\(x\)是质数.

注意到这就是Miller Rabin算法具有随机性的证据之二:对质数的判定不充分.但事实上,用上面这种方法已经可以成功地判定许多质数了,在实际应用中是值得信赖的.

上面这个算法的时间复杂度是\(O((\log n)^2)\)的:瓶颈在于快速幂,和分解待验证的数;但实际运行时,速度是相当快的。

(之前这里是有代码的,但是好像有点bug。这里就不张贴了。)

2.2 算法的优化

显然,我们不难看出上述代码有大量需要优化的地方.就比如说,开始的那次费马小定理的判定是完全没有必要的.我们可以直接把这一过程留到二次探测定理去执行.
其次,我们使用二次探测定理时每次都要探测\(\log n\)次,这个次数是可以稍微有所优化的.

对于前者,我们直接删去开头的费马小定理判断就可以了.对于后者,网上广为流传这样一种优化技巧:

设\(k=x-1=2^p \cdot d\),\(d\)是一奇数,那么之前二次探测定理\(X^2 \equiv 1 \pmod{x}\)检测的数\(X\)分别是如下几个数:\(b^{2^p*d},b^{2^{p-1}*d},b^{2^{p-2}*d},\dots,b^{d}\).
如果我们从后往前检测,如果其中一个数\(X\)通过了二次探测定理,就直接判定\(x\)是质数.

我会尝试着证明这个的.日后可能会贴在这里.

这个新算法的流程如下:
1.按上面的方法计算出d和p.记\(cur=b^d\).如果\(cur\equiv 1 \text{或} x-1 \pmod{x}\)就直接判定x是质数.
2.每次把\(cur\)赋值为\(cur^2 \pmod{x}\)直到\(cur=b^k\).这个过程等价与将\(cur\)从\(b^d\)往\(b^k\)的方向扫描.
3.如果\(cur = x-1\)则直接判定x为质数.否则转2.
4.如果上述测试都没有判定x为质数,则直接判定x为合数.

bool mr(ll x,ll b)
{
    ll d,p=0;
    d=x-1;
    while((d&1)==0)
        d>>=1,++p;
    int i;
    ll cur=qp(b,d,x);//快速幂
    if(cur==1 || cur==x-1)
        return true;
    for(i=1;i<=p;++i)
    {
        cur=cur*cur%x; //为了防止溢出,这里最好用快速乘或者直接用__int128转化一下
        if(cur==x-1)
            return true;
    }
    if(i>p)
        return false;
}
bool mr(ll x)
{
    if(x==46856248255981 || x<2)
        return false;
    if(x==2 || x==3 || x==7 || x==61 || x==24251)
        return true;
    return mr(x,2)&&mr(x,3)&&mr(x,7)&&mr(x,61)&&mr(x,24251);
}

算法的具体实现我也是参考了洛谷P4718大佬@Great_Influence 的题解,不太清楚背后的原因.可以参考一下他的代码.

Miller Rabin 算法简介的更多相关文章

  1. Miller Rabin算法详解

    何为Miller Rabin算法 首先看一下度娘的解释(如果你懒得读直接跳过就可以反正也没啥乱用:joy:) Miller-Rabin算法是目前主流的基于概率的素数测试算法,在构建密码安全体系中占有重 ...

  2. Pollard rho算法+Miller Rabin算法 BZOJ 3668 Rabin-Miller算法

    BZOJ 3667: Rabin-Miller算法 Time Limit: 60 Sec  Memory Limit: 512 MBSubmit: 1044  Solved: 322[Submit][ ...

  3. Miller Rabin算法学习笔记

    定义: Miller Rabin算法是一个随机化素数测试算法,作用是判断一个数是否是素数,且只要你脸不黑以及常数不要巨大一般来讲都比\(O(\sqrt n)\)的朴素做法更快. 定理: Miller ...

  4. 【数论基础】素数判定和Miller Rabin算法

    判断正整数p是否是素数 方法一 朴素的判定   

  5. (Miller Rabin算法)判断一个数是否为素数

    1.约定 x%y为x取模y,即x除以y所得的余数,当x<y时,x%y=x,所有取模的运算对象都为整数. x^y表示x的y次方.乘方运算的优先级高于乘除和取模,加减的优先级最低. 见到x^y/z这 ...

  6. Miller Rabin素数检测与Pollard Rho算法

    一些前置知识可以看一下我的联赛前数学知识 如何判断一个数是否为质数 方法一:试除法 扫描\(2\sim \sqrt{n}\)之间的所有整数,依次检查它们能否整除\(n\),若都不能整除,则\(n\)是 ...

  7. POJ1811- Prime Test(Miller–Rabin+Pollard's rho)

    题目大意 给你一个非常大的整数,判断它是不是素数,如果不是则输出它的最小的因子 题解 看了一整天<初等数论及其应用>相关部分,终于把Miller–Rabin和Pollard's rho这两 ...

  8. poj 1811 Pallor Rho +Miller Rabin

    /* 题目:给出一个数 如果是prime 输出prime 否则输出他的最小质因子 Miller Rabin +Poller Rho 大素数判定+大数找质因子 后面这个算法嘛 基于Birthday Pa ...

  9. 与数论的厮守01:素数的测试——Miller Rabin

    看一个数是否为质数,我们通常会用那个O(√N)的算法来做,那个算法叫试除法.然而当这个数非常大的时候,这个高增长率的时间复杂度就不够这个数跑了. 为了解决这个问题,我们先来看看费马小定理:若n为素数, ...

随机推荐

  1. [转帖]英特尔的 ME 或侵犯 Minix3 的自由软件许可证

    英特尔的 ME 或侵犯 Minix3 的自由软件许可证 [日期:2017-12-11] 来源:Linux公社  作者:非非然 [字体:大 中 小] https://www.linuxidc.com/L ...

  2. Codeforces 1194B. Yet Another Crosses Problem

    传送门 直接枚举填满哪一行,然后看看这一行填满以后哪一列最小 这个预处理一下 $cnt[i]$ 表示初始时第 $i$ 列有几个位置填满就可以做到 $O(m)$ 对于所有情况取个 $min$ 就是答案, ...

  3. org.apache.shiro.realm.AuthorizingRealm - No cache or cacheManager properties have been set. Authorization cache cannot be obtained.

    项目中用spring shiro来处理权限的问题,但是启动的时候会打印如下日志 org.apache.shiro.realm.AuthorizingRealm - No cache or cacheM ...

  4. MySQL性能优化(一):优化方式

    原文:MySQL性能优化(一):优化方式 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/v ...

  5. SQL----Group By and Having

    合计函数 (比如 SUM) 常常需要添加 GROUP BY 语句. GROUP BY 语句 GROUP BY 语句用于结合合计函数,根据一个或多个列对结果集进行分组. SQL GROUP BY 语法 ...

  6. Mysql学习(三)之数据库管理工具Navicat

    前言 mysql安装完后默认只有命令行工具,所以我们可以下载一些数据库管理工具Navicat Navicat使用 首先建立一个连接选择mysql,填写信息 发现多了一个localhost,双击,打开连 ...

  7. pytorch中torch.narrow()函数

    torch.narrow(input, dim, start, length) → Tensor Returns a new tensor that is a narrowed version of  ...

  8. WPF:元素绑定

    到目前为止都在讨论如何链接两个元素的绑定.但在数据驱动的应用程序中,更常见的情况是创建从不可见的对象中提取数据绑定表达式.唯一的要求是希望显示的信息必须存储在公有的属性中.WPF数据绑定基础结构不能获 ...

  9. TCP/IP模型层次结构

    计算机网络体系结构 (1)OSI七层协议:从上到下:应用层.表示层.会话层.传输层.网络层.数据链路层.物理层. (2)TCP/IP四层协议:从上到下:应用层,传输层.网络层.数据链路层.网络接口层. ...

  10. jquery ready load

    jq 加载三种写法 $(document).ready(function() { // ...代码... }) //document ready 简写 $(function() { // ...代码. ...