数论 Pollard_Rho算法

1.1作用

Pollard_Rho算法解决大数的质因数分解。又是一个玄学算法..

2.1 试除法

我们的任务是对一个数字n进行质因数分解。可以发现,n的因数将会对称的分布在[1,sqrt(n)],和[sqrt(n),n]两个区间中,我们只需对前者扫一遍,即可求出所有的质因数,复杂度为\(0(\sqrt(n))\)

但是,假如数据范围是longlong范围呢?1e18呢?试除法似乎有点捉襟见肘...

2.2 一个显而易见的错误随机方法

emm,很明显,完整的扫一遍是不可能的,尝试随机一下。假设数字为在1e18范围内充斥着两个质因数,我们随机来一发,会有多大的几率中奖?的确,我们有一定的概率会得到一个因数,但概率显而易见不在我们可以接受的范围内。

3.1 生日悖论

生日悖论,指如果一个房间里有23个或23个以上的人,那么至少有两个人的生日相同的概率要大于50%。这就意味着在一个典型的标准小学班级(30人)中,存在两人生日相同的可能性更高。对于60或者更多的人,这种概率要大于99%。

这个生日悖论,条件显而易见,证明方法显而易见,只是因为与常识有所违背,所以称之为"悖论"。

这个悖论告诉我们的精华在于:在[1,n]内的数字中随机抽取数字,使之与指定数字p相同,抽取\(\sqrt(n)\)次便有了50%的概率;抽取次数越高,概率越可观。即,在范围内抽出数字使之符合期望是一件可以实现的事情。

3.2 基于生日悖论的随机方法改进

这样子看来,虽然随机一遍成功的概率微乎其微,但通过多次随机,其期望步骤在可接受范围内。

但是,眼前还有很多问题没有解决

[1] 怎么随机出合适的数字?直接随机?

[2] 怎么合理的判断是否是因数?

4.1 Pollard_Rho算法

对于上述问题算法,Pollard_Rho算法提出了一个切实可行的方法。

引出一个函数如下:

\[f(n)=(n^2+c) \quad mod\ x
\]

c是一个随机的常数,范围在[1,x-1];x是当前进行质因数分解的数字。

从0开始,代入函数,并将结果再次代入函数,会顺序地得到一个数列a[..]。由于函数的特性,在一定范围内可以保证数列的随机性。数列中相邻两个数字做差,就可以得到一个随机数字n。求d=gcd(n,x),倘若d大于1,d便是x的一个因数。

由于函数的某种玄学特性,这个做法相比普通随机可以节省大量时间。具体证明就..鸽了..╰( ̄ω ̄o)

此时,我们得到了x的一个因数d,但这显然是不够的。我们需要将d和x/d继续分解,直到全部分解为质因数。

4.2 floyd判环

你知道为什么这个算法叫\(Pollard \quad \rho\)吗?(罗马字母\(\rho\)发音rho)因为通过这个函数得到数列可能会存在循环节,倘若出现了循环节,我们将反反复复进行大量无用尝试,而无法得到结果。因此,我们需要一个有效的判环操作。

通过生日悖论可知,这个环的期望长度在\(\sqrt n\)级别,但是我还是不会证明

floyd判环的思想在于设两个指针,让两个指针同时以不同的速率进行运动,这样的话倘若两个指针进入了环,期望只需要\(\sqrt n\)步,两个指针便会再次重逢,此时退出循环。枚举不同的c,并再次尝试。

既然我们已经有了两个指针了,就不再拘泥于必须要相邻的两个数字。两个指针的结果作差取绝对值即可。

4.3 基于floyd判环补充、生日悖论佐证的随机算法

至此,Pollard_Rho算法的基础理论,流程框架已经完整地展现出来了。

[1] 先用Millar_Rabin判断是否为质数,如果是质数说明已经到头;否则进行接下来的操作。

[2] 用函数生成数列,得到随机数n,在gcd(n,x)>1时,得到一个因数gcd(n,x)

[3] 倘若因为不合适的c导致了数列出现了循环节,利用floyd判环,及时退出即可

[42] 将x拆分为x和x/gcd(n,x),递归下去

5.1 路径倍增优化

求解一次gcd的复杂度是\(o(log\ n)\),大量求解gcd必然会降低效率。因此我们要慎重使用gcd

倘若使用频率过低,可能会出现出现了答案却不能及时退出;使用频率过高,效率就会显著受到影响

那么,就倍增路径!就是这么直接( ¯(∞)¯ )

每\(2^i\)步判断一次gcd,在此之前,递乘每一次的结果。由于某种特殊神秘的性质,对结果取模并不会对gcd产生影响。因此可以放心地取模

5.2 127优化

这个,从洛谷上看到的。尝试了一下发现确实虽然不愿意承认有实际效果。(ac掉最后一个tle点)

方法是:每127步求一遍gcd。

有点难以解释..127除了是质数以外,似乎没有其它特殊的性质。就当作一个玄学优化技巧吧。

6.1 代码

在洛谷上有Pollard_Rho的板子题,代码如下:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef long double ld;
typedef unsigned long long ull; const int MAX=1e6+5,TOP=1e4;
bool vis[MAX]; int prime[MAX];
ll max_factor; int T; ll n; ll read();
void oula();
ll qsm(ll a,ll k,ll m);
ll qsc(ll a,ll b,ll m);
bool millar_rabin(ll);
void pollard_rho(ll);
ll frac(ll);
ll func(ll,ll,ll);
ll gcd(ll a,ll b); int main(){
#ifndef ONLINE_JUDGE
freopen("test.in","r",stdin);
#endif oula(); scanf("%d",&T);
while(T--){
n=read();
max_factor=0;
pollard_rho(n);
if(max_factor==n) printf("Prime\n");
else printf("%lld\n",max_factor);
} return 0;
} ll read(){
char tmp=getchar(); ll sum=0; bool flag=false;
while(tmp<'0'||tmp>'9'){
if(tmp=='-') flag=true;
tmp=getchar();
}
while(tmp>='0'&&tmp<='9'){
sum=(sum<<1)+(sum<<3)+tmp-'0';
tmp=getchar();
}
return flag?-sum:sum;
} void oula(){
for(int i=2;i<=TOP;++i){
if(!vis[i]) prime[++prime[0]]=i;
for(int j=1;j<=prime[0]&&prime[j]*i<=TOP;++j){
vis[prime[j]*i]=true;
if(i%prime[j]==0) break;
}
}
} ll qsm(ll a,ll k,ll m){
ll ans=1,base=a,mod=m;
while(k){
// if(k&1) ans=ans*base%mod;
if(k&1) ans=qsc(ans,base,mod);
// base=base*base%mod;
base=qsc(base,base,mod);
k>>=1;
}
return ans;
} ll qsc(ll a,ll b,ll m){
// ll ans=0,base=a,mod=m;
// while(b){
// if(b&1) ans=(ans+base)%mod;
// base=(base+base)%mod;
// b>>=1;
// }
// return ans; ll z=(ld)a/m*b;
ll res=(ull)a*b-(ull)z*m;
return (res+m)%m; return ((ull)a*b-(ull)((ld)a/m*b*b))%m;
} bool millar_rabin(ll x){
if(x==2) return true;
if(!(x&1)||x==0||x==1) return false;
ll s=0,t=x-1;
while(!(t&1)) s++,t>>=1;
for(int p=1;p<=30&&prime[p]<x;++p){
ll b=qsm((ll)prime[p],t,x),k;
for(ll i=1;i<=s;++i){
k=qsc(b,b,x);
if(k==1&&b!=1&&b!=x-1) return false;
b=k;
}
if(b!=1) return false;
}
return true;
} void pollard_rho(ll x){
if(x==1) {max_factor=1; return;}
if(millar_rabin(x)) {max_factor=max(x,max_factor); return;}
ll p;
do{
p=frac(x);
}while(p>=x);
while(x%p==0) x/=p;
if(x!=1) pollard_rho(x); pollard_rho(p);
} ll frac(ll x){
ll s,t,c,d;
c=rand()%(x-1)+1;
s=0; t=func(0,c,x);
ll goal=1,step=0,val=1;
while(s!=t){
step++;
val=qsc(val,abs(s-t),x);
if(step==goal){
d=gcd(val,x);
if(d>1) return d;
goal<<=1ll;
}
s=func(s,c,x);
t=func(func(t,c,x),c,x);
}
return x;
} ll func(ll x,ll c,ll mod){
return (qsc(x,x,mod)+c)%mod;
} ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}

7.1 引用资料

一个非常有用的pdf

Pollard_Rho算法的更多相关文章

  1. 数论 - Miller_Rabin素数测试 + pollard_rho算法分解质因数 ---- poj 1811 : Prime Test

    Prime Test Time Limit: 6000MS   Memory Limit: 65536K Total Submissions: 29046   Accepted: 7342 Case ...

  2. 【转】大素数判断和素因子分解【miller-rabin和Pollard_rho算法】

    集训队有人提到这个算法,就学习一下,如果用到可以直接贴模板,例题:POJ 1811 转自:http://www.cnblogs.com/kuangbin/archive/2012/08/19/2646 ...

  3. 数学#素数判定Miller_Rabin+大数因数分解Pollard_rho算法 POJ 1811&2429

    素数判定Miller_Rabin算法详解: http://blog.csdn.net/maxichu/article/details/45458569 大数因数分解Pollard_rho算法详解: h ...

  4. Miller_Rabbin算法判断大素数,Pollard_rho算法进行质因素分解

    Miller-rabin算法是一个用来快速判断一个正整数是否为素数的算法.它利用了费马小定理,即:如果p是质数,且a,p互质,那么a^(p-1) mod p恒等于1.也就是对于所有小于p的正整数a来说 ...

  5. pollard_rho 算法进行质因数分解

    //************************************************ //pollard_rho 算法进行质因数分解 //*********************** ...

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

    POJ1811 给一个大数,判断是否是素数,如果不是素数,打印出它的最小质因数 随机素数测试(Miller_Rabin算法) 求整数素因子(Pollard_rho算法) 科技题 #include< ...

  7. 大数因式分解 Pollard_rho 算法详解

    给你一个大数n,将它分解它的质因子的乘积的形式. 首先需要了解Miller_rabin判断一个数是否是素数 大数分解最简单的思想也是试除法,这里就不再展示代码了,就是从2到sqrt(n),一个一个的试 ...

  8. 大素数判断和素因子分解(miller-rabin,Pollard_rho算法) 玄学快

    大数因数分解Pollard_rho 算法 复杂度o^(1/4) #include <iostream> #include <cstdio> #include <algor ...

  9. 大素数判断和素因子分解(miller-rabin,Pollard_rho算法)

    #include<stdio.h> #include<string.h> #include<stdlib.h> #include<time.h> #in ...

  10. Pollard_rho算法进行质因素分解

    Pollard_rho算法进行质因素分解要依赖于Miller_Rabbin算法判断大素数,没有学过的可以看一下,也可以当成模板来用 讲一下Pollard_rho算法思想: 求n的质因子的基本过程是,先 ...

随机推荐

  1. postman一些你不常用的实用技巧,竟然还能这么玩

    序言 各位好啊,我是会编程的蜗牛,作为java开发者,平时调试接口的时候,肯定需要用到接口调试工具,或者Swagger之类的.Swagger的优势在于它可以将后台加的一些接口注释信息直接展示出来,但是 ...

  2. 畸变矫正、透视变换加速(OpenCV C++)

    前两周,同事和我说检测时间超时,其中对图像做畸变矫正和投影变换就要花费25ms(3000×3000的图).而此时我们已经用上了文章opencv图像畸变矫正加速.透视变换加速方法总结中的方法.突然我想到 ...

  3. HTML元素大全(1)

    01.基础元素 <h1/2/3/4/5/6>标题 从大h1到小h6,块元素,有6级标题.是一种标题类语义标签,内置了字体.边距样式. 合理使用h标签,主要用于标题,不要为了加粗效果而随意使 ...

  4. <五>掌握左值引用和初识右值引用

    1:C++的引用,引用和指针的区别? 1:从汇编指令角度上看,引用和指针没有区别,引用也是通过地址指针的方式访问指向的内存 int &b=a ; 是需要将a的内存地址取出并存下来, b=20; ...

  5. Linux三剑客awk

    Linux三剑客awk awk是一个强大的linux命令,有强大的文本格式化的能力,好比将一些文本数据格式化成专业的excel表的样式 awk早期在Unix上实现,我们用的awk是gawk,是GUN ...

  6. 八、Django的组件

    8.1.中间件 中间件顾名思义,是介于request与response处理之间的一道处理过程,相对比较轻量级,并且在全局上改变django的输入与输出.因为改变的是全局,所以需要谨慎实用,用不好会影响 ...

  7. Java反序列化中jndi注入的高版本jdk绕过

    群里大佬们打哈哈的内容,菜鸡拿出来整理学习一下,炒点冷饭. 主要包含以下三个部分: jndi注入原理 jndi注入与反序列化 jndi注入与jdk版本 jndi注入原理: JNDI(Java Name ...

  8. 我的 React 最佳实践

    There are a thousand Hamlets in a thousand people's eyes. ----- 威廉·莎士比亚 免责声明:以下充满个人观点,辩证学习 React 目前开 ...

  9. Echarts - legend属性设置

    legend: { orient: 'horizontal', // 'vertical' x: 'right', // 'center' | 'left' | {number}, y: 'top', ...

  10. 幂次方表达:p1010

    1 题目ID: P1010 [NOIP1998 普及组] 幂次方 2 题目描述: 任何一个正整数都可以用 22 的幂次方表示.例如 137=2^7+2^3+2^0137=27+23+20. 同时约定方 ...