Miller Rabin 算法简介
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 算法简介的更多相关文章
- Miller Rabin算法详解
何为Miller Rabin算法 首先看一下度娘的解释(如果你懒得读直接跳过就可以反正也没啥乱用:joy:) Miller-Rabin算法是目前主流的基于概率的素数测试算法,在构建密码安全体系中占有重 ...
- Pollard rho算法+Miller Rabin算法 BZOJ 3668 Rabin-Miller算法
BZOJ 3667: Rabin-Miller算法 Time Limit: 60 Sec Memory Limit: 512 MBSubmit: 1044 Solved: 322[Submit][ ...
- Miller Rabin算法学习笔记
定义: Miller Rabin算法是一个随机化素数测试算法,作用是判断一个数是否是素数,且只要你脸不黑以及常数不要巨大一般来讲都比\(O(\sqrt n)\)的朴素做法更快. 定理: Miller ...
- 【数论基础】素数判定和Miller Rabin算法
判断正整数p是否是素数 方法一 朴素的判定
- (Miller Rabin算法)判断一个数是否为素数
1.约定 x%y为x取模y,即x除以y所得的余数,当x<y时,x%y=x,所有取模的运算对象都为整数. x^y表示x的y次方.乘方运算的优先级高于乘除和取模,加减的优先级最低. 见到x^y/z这 ...
- Miller Rabin素数检测与Pollard Rho算法
一些前置知识可以看一下我的联赛前数学知识 如何判断一个数是否为质数 方法一:试除法 扫描\(2\sim \sqrt{n}\)之间的所有整数,依次检查它们能否整除\(n\),若都不能整除,则\(n\)是 ...
- POJ1811- Prime Test(Miller–Rabin+Pollard's rho)
题目大意 给你一个非常大的整数,判断它是不是素数,如果不是则输出它的最小的因子 题解 看了一整天<初等数论及其应用>相关部分,终于把Miller–Rabin和Pollard's rho这两 ...
- poj 1811 Pallor Rho +Miller Rabin
/* 题目:给出一个数 如果是prime 输出prime 否则输出他的最小质因子 Miller Rabin +Poller Rho 大素数判定+大数找质因子 后面这个算法嘛 基于Birthday Pa ...
- 与数论的厮守01:素数的测试——Miller Rabin
看一个数是否为质数,我们通常会用那个O(√N)的算法来做,那个算法叫试除法.然而当这个数非常大的时候,这个高增长率的时间复杂度就不够这个数跑了. 为了解决这个问题,我们先来看看费马小定理:若n为素数, ...
随机推荐
- [转帖]英特尔的 ME 或侵犯 Minix3 的自由软件许可证
英特尔的 ME 或侵犯 Minix3 的自由软件许可证 [日期:2017-12-11] 来源:Linux公社 作者:非非然 [字体:大 中 小] https://www.linuxidc.com/L ...
- Codeforces 1194B. Yet Another Crosses Problem
传送门 直接枚举填满哪一行,然后看看这一行填满以后哪一列最小 这个预处理一下 $cnt[i]$ 表示初始时第 $i$ 列有几个位置填满就可以做到 $O(m)$ 对于所有情况取个 $min$ 就是答案, ...
- 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 ...
- MySQL性能优化(一):优化方式
原文:MySQL性能优化(一):优化方式 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csdn.net/v ...
- SQL----Group By and Having
合计函数 (比如 SUM) 常常需要添加 GROUP BY 语句. GROUP BY 语句 GROUP BY 语句用于结合合计函数,根据一个或多个列对结果集进行分组. SQL GROUP BY 语法 ...
- Mysql学习(三)之数据库管理工具Navicat
前言 mysql安装完后默认只有命令行工具,所以我们可以下载一些数据库管理工具Navicat Navicat使用 首先建立一个连接选择mysql,填写信息 发现多了一个localhost,双击,打开连 ...
- pytorch中torch.narrow()函数
torch.narrow(input, dim, start, length) → Tensor Returns a new tensor that is a narrowed version of ...
- WPF:元素绑定
到目前为止都在讨论如何链接两个元素的绑定.但在数据驱动的应用程序中,更常见的情况是创建从不可见的对象中提取数据绑定表达式.唯一的要求是希望显示的信息必须存储在公有的属性中.WPF数据绑定基础结构不能获 ...
- TCP/IP模型层次结构
计算机网络体系结构 (1)OSI七层协议:从上到下:应用层.表示层.会话层.传输层.网络层.数据链路层.物理层. (2)TCP/IP四层协议:从上到下:应用层,传输层.网络层.数据链路层.网络接口层. ...
- jquery ready load
jq 加载三种写法 $(document).ready(function() { // ...代码... }) //document ready 简写 $(function() { // ...代码. ...