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,a,mod);
		a=qmul(a,a,mod);
		n>>=1;
	}return ans;
}
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;
}
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;
    }
}
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);
}	
int main(){
	    srand((unsigned)time(NULL));
		ll n;scanf("%lld",&n);
		if(n==1) puts("1");
		else if(MR(n)) puts("2");
		else {
			dfs(n);
			ll ans=1;
			for(auto [x,y]:mp) ans*=(y+1);
			printf("%lld\n",ans);
		}
	
	return 0;
}

3.质因数分解

上题的求因数个数,就是基于算术基本定理的,所以质因数分解也简单。

在上题的基础上,把main函数改下,即可。

#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 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;
}
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;
    }
}
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);
}	
int main(){
	    srand((unsigned)time(NULL));
		ll n;scanf("%lld",&n);
		if(n==1) puts("1");
		else if(MR(n)) puts("2");
		else {
			dfs(n);
			ll ans=1;
			for(auto [x,y]:mp){	//质因数x 的出现次数y
				printf("%lld %lld\n",x,y);
			}
		}
	
	return 0;
}

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
好的,以下是使用 Python 语言实现 Pollard Rho 算法进行因式分解的代码: ```python import random def gcd(a, b): while b != 0: a, b = b, a % b return a def pollard_rho(n): x = random.randint(1, n-1) y = x c = random.randint(1, n-1) d = 1 while d == 1: x = (x ** 2 + c) % n y = (y ** 2 + c) % n y = (y ** 2 + c) % n d = gcd(abs(x - y), n) if d == n: return pollard_rho(n) return d def factorize(n): factors = [] while n > 1: factor = pollard_rho(n) factors.append(factor) n //= factor return factors n = 1234567890 factors = factorize(n) print(factors) ``` 在这个代码中,我们使用了 Pollard Rho 算法进行因式分解。首先,我们定义了一个辗转相除法的函数 gcd(a, b),用于求两个数的最大公约数。然后,我们定义了一个 pollard_rho(n) 函数,该函数接受一个正整数 n 作为参数,并返回 n 的一个因子。该函数通过随机选择 x 和 c,然后使用 f(x) = x^2 + c 的迭代公式来产生一个序列 x_0, x_1, x_2, ...,并使用 Floyd 算法来寻找序列中的循环节,最后找到两个序列中的相同元素并计算它们的差的绝对值和 n 的最大公约数,如果这个最大公约数不是 1 或 n,则表示我们已经找到了 n 的一个因子。如果这个最大公约数是 1 或 n,则需要重新选取 x 和 c,进行迭代。最后,我们定义了一个 factorize(n) 函数,该函数接受一个正整数 n 作为参数,并返回 n 的所有因子,该函数通过重复调用 pollard_rho(n) 函数来实现。最后,我们测试了这个函数,并输出了 n 的所有因子。 希望这个代码对你有所帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

酷酷的Herio

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值