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的因数。

所以有下面这个:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-I8jZ3qRh-1625578357960)(C:\\Users\\HeHao\\AppData\\Roaming\\Typora\\typora-user-images\\image-20210706204812915.png)]

使用如下函数,生成一个伪随机数列

在这里插入图片描述


在这里插入图片描述

因为该数列的伪随机性,会产生一个循环节。所以我们要对该算法进行优化,采用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)>1gcd(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 复习总结

大数质因解:浅谈Miller-Rabin和Pollard-Rho算法

算法之我见 [数论之旅] Pollard Rho因数分解

P4718Pollard-Rho算法

随机素数测试(Miller_Rabin算法)和求整数素因子(Pollard_rho算法)