Pollard rho算法总结
Posted Harris-H
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Pollard rho算法总结相关的知识,希望对你有一定的参考价值。
Pollard rho算法总结
前置知识
- 快速幂
- 快速乘
- Miller Rrabin判断素数 O ( k l o g 3 n ) O(klog^3n) O(klog3n)
快速幂
太简单了,不讲
ll ksm(ll a,ll n,ll p){
ll s=1;
while(n){
if(n&1) s=qmul(s,a,p);
a=qmul(a,a,p);
n>>=1;
}
return s;
}
快速乘
比较简单,就转化为long double 在转换为longlong 自然溢出 差值保证正确,再取模即可。
ll qmul(ll a,ll b,ll p){
return ((a*b-(ll)(LD(a)/p*b)*p)%p+p)%p;
}
Miller Rrabin 判素数 O ( k l o g 3 n ) O(klog^3n) O(klog3n)
原理见下面博客:
主要利用费马小定理和二次探测定理。
ll d[8]={2,3,5,7,11,13,79,97};
bool MR(ll n){
if(n<3) return n==2;
if(n%2==0) return 0;
ll p=n-1,c=0;
while(p%2==0) p>>=1,c++;
for(int i=0;i<8;i++){
if(d[i]==n) return 1;
ll x=ksm(d[i],p,n),y=x;
for(int j=0;j<c;j++){
x=qmul(x,x,n);
if(x==1&&!(y==1||y==n-1)) return 0;
y=x;
}
if(x!=1) return 0;
}
return 1;
}
需要注意的是,二次探测定理是质数的必要条件,如果不满足必定不是质数,且费马小定理也是质数的必要条件,不满足必定不是。
Pollard Rho部分
利用生日悖论的原理,生成一段随机数列,采用组合来提高正确率。
具体的依据是:如果 g c d ( a , n ) ∣ n gcd(a,n)|n gcd(a,n)∣n,则 a a a是 n n n的因数。
所以有下面这个:
使用如下函数,生成一个伪随机数列。
因为该数列的伪随机性,会产生一个循环节。所以我们要对该算法进行优化,采用floyd判圈的方法,也就是龟兔赛跑问题的优化。
因为会频繁调用gcd函数,导致算法时间复杂度增加,所以基于gcd,
有 g c d ( a , n ) > 1 → g c d ( a b , n ) > 1 gcd(a,n)>1\\rightarrow gcd(ab,n)>1 gcd(a,n)>1→gcd(ab,n)>1,所以我们可以先把所有测试的样本都乘起来,最后在取模意义的 g c d ( a b . . . ( m o d n ) ) , n ) > 1 gcd(ab...\\pmod n),n)>1 gcd(ab...(modn)),n)>1即可。
见大佬的讲解:
这个样本的上界设置为 127 127 127。
基于上面的方法和优化,可以得到一个优化的PR算法。
代码和具体解释见下面:
code
inline ll f(ll x,ll c,ll m) //生成随机数列
{
return (__int128(x)*x+c)%m;
}
ll PR(ll x) //随机找到x的一个因数
{
ll s=0,t=0,c=1ll*rand()%(x-1)+1;
//t, s 兔子和乌龟(龟兔赛跑问题),即模拟每个样本的两个自变量.
int stp=0,goal=1;
ll val=1;
for(goal=1;;goal<<=1,s=t,val=1)
//采用倍增法,每次测试goal个样本,val每次赋值为1,最后累乘得到的样本结果.
{
for(stp=1;stp<=goal;++stp) //枚举算每个测试样本.
{
t=f(t,c,x);
val=__int128(val)*abs(t-s)%x;//得到差累积起来.只进行一次判断.
if((stp%127)==0) //样本个数超过127就直接判断.
{
ll d=gcd(val,x);
if(d>1)
return d;
}
}
ll d=gcd(val,x);
if(d>1)
return d;
}
}
应用
1.求解大数的最大质因数
算法流程
主要通过递归查找实现。
- 首先利用MR算法 特判当前数 x x x是否为质数,如果是质数的话,则ans取max。
- 否则,利用PR算法找到一个小于x的因数p,采用while不断PR查找,直到小于x。
- 将x不断除以该因数。
- 递归查找dfs(x),dfs§
这里解释一下为什么要x不断除以该因数,因为p可以通过dfs§ 判断,所以x不再需要x。
ll max_factor;
inline void dfs(ll x) //递归找x的最大因数
{
if(x<=max_factor || x<2)
return;
if(fun(x))
{
max_factor=max_factor>x?max_factor:x;
return;
}
ll p=x;
while(p>=x) //找到一个小于x的因数.
p=PR(x);
while((x%p)==0) //不断整除.
x/=p;
dfs(x),dfs(p);
}
P4718 【模板】Pollard-Rho算法
code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
ll gcd(ll a,ll b){
return !b?a:gcd(b,a%b);
}
inline ll qmul(ll a,ll b,ll mod){
return (ll)(__int128(a)*b%mod);
}
ll ksm(ll a,ll n,ll mod){
ll ans=1;
while(n){
if(n&1) ans=qmul(ans,a,mod);
a=qmul(a,a,mod);
n>>=1;
}return ans;
}
ll d[8]={2,3,5,7,11,13,79,97};
bool fun(ll n){
if(n<3) return n==2;
if(n%2==0) return 0;
ll p=n-1,c=0;
while(p%2==0) p>>=1,c++;
for(int i=0;i<8;i++){
if(d[i]==n) return 1;
ll x=ksm(d[i],p,n),y=x;
for(int j=0;j<c;j++){
x=qmul(x,x,n);
if(x==1&&!(y==1||y==n-1)) return 0;
y=x;
}
if(x!=1) return 0;
}return 1;
}
inline ll f(ll x,ll c,ll m) //生成随机数列
{
return (__int128(x)*x+c)%m;
}
ll PR(ll x) //随机找到x的一个因数
{
ll s=0,t=0,c=1ll*rand()%(x-1)+1;
//t, s 兔子和乌龟(龟兔赛跑问题),即模拟每个样本的两个自变量.
int stp=0,goal=1;
ll val=1;
for(goal=1;;goal<<=1,s=t,val=1)
//采用倍增法,每次测试goal个样本,val每次赋值为1,最后累乘得到的样本结果.
{
for(stp=1;stp<=goal;++stp) //枚举算每个测试样本.
{
t=f(t,c,x);
val=__int128(val)*abs(t-s)%x;//得到差累积起来.只进行一次判断.
if((stp%127)==0) //样本个数超过127就直接判断.
{
ll d=gcd(val,x);
if(d>1)
return d;
}
}
ll d=gcd(val,x);
if(d>1)
return d;
}
}
ll max_factor;
inline void dfs(ll x) //递归找x的最大因数
{
if(x<=max_factor || x<2)
return;
if(fun(x))
{
max_factor=max_factor>x?max_factor:x;
return;
}
ll p=x;
while(p>=x) //找到一个小于x的因数.
p=PR(x);
while((x%p)==0) //不断整除.
x/=p;
dfs(x),dfs(p);
}
ll T;
int main(){
scanf("%lld",&T);
while(T--){
srand((unsigned)time(NULL));
ll n;scanf("%lld",&n);
max_factor=0;
dfs(n);
if(max_factor==n)
puts("Prime");
else
printf("%lld\\n",max_factor);
}
return 0;
}
2.求大数的因数个数
先来理性分析一波复杂度,利用PR快速找到一个质因数,最坏情况下质因数全部为2,需要找 l o g ( n ) log(n) log(n)次,所以最多找 l o g ( n ) log(n) log(n)个质数。
结合PR复杂度: n 1 4 n^{\\small\\dfrac{1}{4}} n41。
质因数分解部分
unordered_map<ll,ll>mp;
inline void dfs(ll n){
if(MR(n)){
mp[n]++;return;
}
ll p=n;
while(p>=n) p=PR(n);
dfs(p),dfs(n/p);
}
完整代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
ll gcd(ll a,ll b){
return !b?a:gcd(b,a%b);
}
inline ll qmul(ll a,ll b,ll mod){
return (ll)(__int128(a)*b%mod);
}
ll ksm(ll a,ll n,ll mod){
ll ans=1;
while(n){
if(n&1) ans=qmul(ans,aPollard Rho算法浅谈
pollard_rho 学习总结 Miller_Rabbin 复习总结