Miller-Rabin 与 Pollard-Rho 算法学习笔记
前言
Miller-Rabin 算法用于判断一个数 \(p\) 是否是质数,若选定 \(w\) 个数进行判断,那么正确率约是 \(1-\frac{1}{4^w}\) ,时间复杂度为 \(O(\log p+w\log p)\)。(我的实现)
Pollard-Rho 算法可以在期望 \(O(n^{\frac{1}{4}})\) 的时间复杂度内找到合数 \(n\) 的某一个非平凡的(即既不是 \(1\),也不是它本身的)因子。
下文中用 \(\mathbb{P}\) 来表示质数集合。
Miller-Rabin 算法
前置知识
费马小定理:若 \(p\in\mathbb{P},\gcd(a,p)=1\),则 \(a^{p-1}\equiv1\pmod{p}\)。
二次探测定理:若 \(p\in\mathbb{P},x^2\equiv 1\pmod{p}\),则 \(x\equiv\pm1\pmod{p}\)。
注意:费马小定理的逆命题并不成立!
算法流程
首先,将 \(p-1\) 表示成 \(t2^k\) 的形式。那么,若 \(p\in\mathbb{P}\),则 \(p^{t2^k}=p^{p-1}\equiv1\pmod{p}\)。
然后我们选择 \(w\) 个数 \(q_1,q_2,\cdots,q_w\) 进行判断。假如当前判断到了 \(q_i\),那么用快速幂计算出 \(a=q_{i}^{t}\bmod{p}\)。然后让 \(a\) 自乘 \(k\) 次,就可以得到 \(p-1\)。
自乘的时候我们判断,如果 \(a\equiv1\pmod{p}\) ,那么此时 \(p\) 有一定概率是质数。于是我们看一看 \(a\) 自乘前是否满足二次探测定理即可,如果是,则继续自乘,否则表明 \(p\) 一定不是质数。
如果自乘得到的数同余 \(p\) 不为 \(1\),那么 \(p\) 也不一定是质数。否则 \(p\) 很可能是合数。
这时您可能会说:不是说过费马小定理逆定理不成立吗?其实,逆定理的反例(卡迈克尔数)是十分稀少的。经过多次判断,合数判成质数的概率十分小(质数不可能判成合数,想一想,为什么)
OI 中可以选择 \(w=9\),\(q\) 为前 \(9\) 个质数。这样子 \(10^{18}\) 范围内一般不会出错。
参考实现
struct {/*Miller Rabin 质数判定算法*/
vector<int> primes= {2,3,5,7,11,13,17,19,23};
bool operator()(int p) {
if (p==1)return 0;
int t,k;
for (t=p-1,k=0; !(t&1); k++,t>>=1);
for (int i : primes) {
if (p==i) return true;
int a=fastpow(i,t,p),b=a;
for (int j=1; j<=k; j++) {
b=M(((__int128)a)*a,p);
if (b==1&&a!=1&&a!=p-1) return false;
a=b;
}
if (a!=1) return false;
}
return true;
}
} MillerRabin;
Pollard-Rho 算法
前置知识
Floyd 判圈算法:该算法可以线性判断一个链表上是否有环。其流程为使用两个指针。一个指针每次跑 \(1\) 条边,另一个指针一次跑 \(2\) 条边,然后相遇的点就在环上。
算法流程
先特判 \(n=4\) 和 \(n\in\mathbb{P}\)。
Pollard-Rho 需要一个伪随机函数 \(f(x,c,n)=x^2+c\bmod{n}\)。其中 \(x\) 表示上一个数,\(c\) 是我们生成的,用于保证随机性的数,\(n\) 是我们需要找因子的数。
可以发现这个函数最后会大概率生成一个混循环序列。如同希腊字母 \(\rho(\texttt{Rho})\) 一般。
先选择一个随机数 \(c\)。两个指针从 \(0\) 出发,我们看成存在一个链表,其中存在边 \((i,f(i,c,n))\)。然后在上面跑 Floyd 判圈算法,在 Floyd 中,如果一个指针在 \(t\),一个指针在 \(k\)。若 \(\gcd(|t-k|,n)\neq1\)。则我们认为 \(\gcd(|t-k|,n)\) 是 \(n\) 的一个因数。如果找到了环,则重新选择一个 \(c\),重复上述流程。
此时时间复杂度期望 \(O(n^{\frac{1}{4}}\log n)\)。
算法优化
上述算法在洛谷板题上只能获得 \(93\) 分(TLE 在了第 \(13\) 个点)。优化迫在眉睫。
我们发现求 \(\gcd\) 的 \(O(\log n)\) 需要被优化。我们可以固定一个 \(W\),跳 \(W\) 次的时候统计 \(|t-k|\) 的乘积 \(p\)。最后和 \(n\) 取一次 \(\gcd\)。然后如果下一次 \(p\) 会到 \(0\),那么也要跳出。因为后面都是 \(0\)。
这样子只要 \(W\gt \log n\) 就可以做到期望 \(O(n^{\frac{1}{4}})\)。我试了一下,貌似 \(W=256\) 表现不错。
可以加一个记忆化,后面有用。
参考实现
mt19937 engine(time(0));
inline int pr_rand(int x,int c,int n) { /*Pollard Rho 算法使用的伪随机数*/
return M(M(((__int128)x)*x,n)-c,n);
}
int pollard_rho(int n) { /*Pollard Rho 算法求一个数的某一个质因子*/
if (prm[n])return prm[n];
if (n==4) return 2;
if (MillerRabin(n)) return n;
uniform_int_distribution<int> randint(3,n-1);
while (1) {
int c=randint(engine);
int t=0,r=0,p=1,q=0;
do{
for(int i=1;i<=256;i++){
t=pr_rand(t,c,n);
r=pr_rand(pr_rand(r,c,n),c,n);
int delta=(t-r)>0?(t-r):(r-t);
if(t==r||(q=M(__int128(p)*delta,n))==0){
break;
}
p=q;
}
int d=__gcd(p,n);
if(d>1) return prm[n]=d;
}while(t!=r);
}
}
P4718 【模板】Pollard's rho算法
简要题意
\(T\) 组数据,每组数据给出一个数 \(n\),如果 \(n\) 是质数,输出 Prime,否则你需要输出 \(n\) 的最大质因子。
\(1 \leq T \leq 350,1 \lt n \leq 10^{18}\)
思路
首先,我们先用一个 Miller-Rabin 算法来判断质数。我们可以用 Pollard-Rho 算法找出所有的因子(当然不用存起来,只需要用一个递归函数,最后 \(\max\) 统计答案)即可。由于唯一分解定理,这个算法是正确的。
注意我们需要加一个记忆化来保证复杂度,否则复杂度爆炸。
代码
关键代码如下(要完整代码的私信):
unordered_map<int,int> mrm;
int max_factor(int n) { /*求一个数的最大质因子*/
if (mrm[n]) return mrm[n];
int factor=pollard_rho(n);
if (factor==n) return mrm[n]=n;
return mrm[n]=max(max_factor(factor),max_factor(n/factor));
}
参考资料
Miller Rabin 算法详解 - 自为风月马前卒 - 博客园
算法学习笔记(55): Pollard-Rho 算法 - 知乎
Miller-Rabin 与 Pollard-Rho 算法学习笔记的更多相关文章
- 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素数检测与Pollard Rho算法
一些前置知识可以看一下我的联赛前数学知识 如何判断一个数是否为质数 方法一:试除法 扫描\(2\sim \sqrt{n}\)之间的所有整数,依次检查它们能否整除\(n\),若都不能整除,则\(n\)是 ...
- Pollard Rho 算法简介
\(\text{update 2019.8.18}\) 由于本人将大部分精力花在了cnblogs上,而不是洛谷博客,评论区提出的一些问题直到今天才解决. 下面给出的Pollard Rho函数已给出散点 ...
- Pollard Rho算法浅谈
Pollard Rho介绍 Pollard Rho算法是Pollard[1]在1975年[2]发明的一种将大整数因数分解的算法 其中Pollard来源于发明者Pollard的姓,Rho则来自内部伪随机 ...
- C / C++算法学习笔记(8)-SHELL排序
原始地址:C / C++算法学习笔记(8)-SHELL排序 基本思想 先取一个小于n的整数d1作为第一个增量(gap),把文件的全部记录分成d1个组.所有距离为dl的倍数的记录放在同一个组中.先在各组 ...
- Manacher算法学习笔记 | LeetCode#5
Manacher算法学习笔记 DECLARATION 引用来源:https://www.cnblogs.com/grandyang/p/4475985.html CONTENT 用途:寻找一个字符串的 ...
- 初学Pollard Rho算法
前言 \(Pollard\ Rho\)是一个著名的大数质因数分解算法,它的实现基于一个神奇的算法:\(MillerRabin\)素数测试(关于\(MillerRabin\),可以参考这篇博客:初学Mi ...
- Johnson算法学习笔记
\(Johnson\)算法学习笔记. 在最短路的学习中,我们曾学习了三种最短路的算法,\(Bellman-Ford\)算法及其队列优化\(SPFA\)算法,\(Dijkstra\)算法.这些算法可以快 ...
- 某科学的PID算法学习笔记
最近,在某社团的要求下,自学了PID算法.学完后,深切地感受到PID算法之强大.PID算法应用广泛,比如加热器.平衡车.无人机等等,是自动控制理论中比较容易理解但十分重要的算法. 下面是博主学习过程中 ...
- Johnson 全源最短路径算法学习笔记
Johnson 全源最短路径算法学习笔记 如果你希望得到带互动的极简文字体验,请点这里 我们来学习johnson Johnson 算法是一种在边加权有向图中找到所有顶点对之间最短路径的方法.它允许一些 ...
随机推荐
- 齐博x1再来个抛砖引玉 内容页根据关键词调用相关内容 新功能哦!
昨天升级了一个隐藏的功能,今天就简单的做个说明怎么用,反正也不能浪费不是 那就用内容页面关键词读取相关内容为例吧. 前台是你模型中已经存在keywords字段 关键词支持 空格分割,号分割 那么就开 ...
- python深拷贝、浅拷贝
.copy() 浅拷贝 如上图 定义列表A指向一个元素,列表A里面嵌套两层列表分布指向两个元素,定义列表B,列表B=A,列表C浅拷贝列表A 从图上可以看出,列表A和列表B指向的是同一个列表元素,而 ...
- iOS App 上架App Store及提交审核详细教程
上架App Store审核分7步进行: 1.安装iOS上架辅助软件Appuploader 2.申请iOS发布证书(p12) 3.申请iOS发布描述文件(mobileprovision) 4.打包ipa ...
- golang中的错误处理
0.1.索引 https://waterflow.link/articles/1666716727236 1.panic 当我们执行panic的时候会结束下面的流程: package main imp ...
- HashMap基本使用方法
HashMap Map集合基于 键(key)/值(value)映射.每个键最多只能映射一个值.键可以是任何引用数据类型的值,不可重复:值可以是任何引用数据类型的值,可以重复:键值对存放无序. Hash ...
- 小菜鸡的学习笔记---<正则表达式(1)>
正则表达式学习笔记(1) (纯新手学习笔记,大佬绕路 QAQ) 一.简介 正则表达式就是一种文本模式用来匹配一系列满足特定条件的字符串,可以对比一下数学里面的表达式,比如我们要用一个表达式表示一串数字 ...
- 如何通过free看懂内存的真实使用
之前有位同事问过Linux系统内存free命令下各参数的区别与关系,自己也没太明白,有点尴尬.今天整理一下,供了解. free命令是Liunx操作系统中对内存进行查看和监控的一个常用命令.我们可以直接 ...
- AIR32F103(五) FreeRTOSv202112核心库的集成和示例代码
目录 AIR32F103(一) 合宙AIR32F103CBT6开发板上手报告 AIR32F103(二) Linux环境和LibOpenCM3项目模板 AIR32F103(三) Linux环境基于标准外 ...
- 【Virt.Contest】CF1155(div.2)
CF 传送门 T1:Reverse a Substring 只有本身单调不减的字符串不能转换为字典序更小的字符串.否则肯定会出现 \(s_i>s_{i+1}\) 的情况. 所以只要从头到尾扫一遍 ...
- C++ undered_map哈希表用法——leetcode两数之和
undered_map 头文件:#include<undered_map> 创建表undered_map<key,value> Map_name; 插入元素 a[key]=va ...