Miller-Rabin 与 Pollard-Rho 算法学习笔记
xiezheyuan · · 题解
Miller-Rabin 与 Pollard-Rho 算法学习笔记
也许有更好的阅读体验
前言
Miller-Rabin 算法用于判断一个数
Pollard-Rho 算法可以在期望
下文中用
Miller-Rabin 算法
前置知识
费马小定理:若
二次探测定理:若
注意:费马小定理的逆命题并不成立!
算法流程
首先,将
然后我们选择
自乘的时候我们判断,如果
如果自乘得到的数同余
这时您可能会说:不是说过费马小定理逆定理不成立吗?其实,逆定理的反例(卡迈克尔数)是十分稀少的。经过多次判断,合数判成质数的概率十分小(质数不可能判成合数,想一想,为什么)
OI 中可以选择
参考实现
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 判圈算法:该算法可以线性判断一个链表上是否有环。其流程为使用两个指针。一个指针每次跑
算法流程
先特判
Pollard-Rho 需要一个伪随机函数
可以发现这个函数最后会大概率生成一个混循环序列。如同希腊字母
先选择一个随机数
此时时间复杂度期望
算法优化
上述算法在洛谷板题上只能获得
我们发现求
这样子只要
另外还可以加一个记忆化,在后面的例题中有用。
参考实现
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);
}
unordered_map<int,int> prm;
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算法
简要题意
思路
首先,我们先用一个 Miller-Rabin 算法来判断质数。我们可以用 Pollard-Rho 算法找出所有的因子(当然不用存起来,只需要用一个递归函数,最后
注意我们需要加一个记忆化来保证复杂度,否则复杂度爆炸。
代码
关键代码如下(要完整代码的私信):
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));
}
AC Record
参考资料
Miller Rabin 算法详解 - 自为风月马前卒 - 博客园
算法学习笔记(55): Pollard-Rho 算法 - 知乎