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. jsp文件

    新建的jsp文件比新建的html文件多了一行内容<%@ page contentType="text/html; charset=utf-8" %>,这样不会显示乱码 ...

  2. # 江西ccpc省赛-waves-(DP做法)

    江西ccpc省赛-waves-(DP做法) 题链:http://acm.hdu.edu.cn/showproblem.php?pid=6570 题意:给你长度为N,1≤N≤100000的一个数组,其中 ...

  3. split、paste命令

    一.split分割文件 语法       split [OPTION] ... [INPUT [PREFIX]] 描述       将固定大小的INPUT输出到PREFIXaa,PREFIXab,.. ...

  4. 搞懂Redis复制原理

    前言 与大多数db一样,Redis也提供了复制机制,以满足故障恢复和负载均衡等需求.复制也是Redis高可用的基础,哨兵和集群都是建立在复制基础上实现高可用的.复制不仅提高了整个系统的容错能力,还可以 ...

  5. leetcode hard

    # Title Solution Acceptance Difficulty Frequency     4 Median of Two Sorted Arrays       27.2% Hard ...

  6. @RequestMapping-限定请求方法的映射

    限定请求方法的映射 测试: 如果非指定的请求方法访问时会出现405状态:

  7. 笔记本电脑重装win7/win10系统教程

    由于笔记本第一次重装系统会出现系统装不上,还有出现找不到有效硬盘分区,等等问题,然后这篇文章主要讲解BIOS设置的方法,用此BIOS设置,电脑用原本安装系统的方式,能有效地解决以上问题,这有两种方法解 ...

  8. Java操作FTP,从FTP上读取指定文件,把指定文件上传到FTP

    需要添加的依赖 <!-- https://mvnrepository.com/artifact/commons-net/commons-net --> <dependency> ...

  9. enum:python实现枚举也很优雅

    介绍 enum是一个用来枚举的模块 创建枚举类型 import enum # 创建一个类,继承自enum下的Enum class Color(enum.Enum): red = 1 green = 2 ...

  10. 10 Zabbix4.4.1系统告警“Zabbix server is not running”

    点击返回:自学Zabbix之路 点击返回:自学Zabbix4.0之路 点击返回:自学zabbix集锦 Zabbix4.4.1系统告警“Zabbix server is not running” 第一步 ...