当前位置: 代码迷 >> 综合 >> 【数论】Pollard-Rho 算法总结
  详细解决方案

【数论】Pollard-Rho 算法总结

热度:91   发布时间:2023-11-21 06:52:24.0

Pollard-Rho 算法总结

预备知识

  1. 质因数分解;
  2. 生日悖论。

然后就愉快的开始吧。

Part.I 如何对一个大合数进行质因数分解

设有一个大合数N=p×q,(p=?q)N=p\times q,(p\not= q)N=p×q,(p??=q),那么我们可以用什么方法去把它分成两个数的乘积呢?

显然我们可以只找到ppp或者qqq,另一个用NNN去除一下就好了。

那么如何去找这个数?显然可以用暴力方法去找 (代码过于简单就不给了)

显然这是要爆炸的。。。

Part.II 随机化???

考虑随机数???

显然这个复杂度瞬间达到了O(O(O(玄学)))

但是这就是Pollard-Rho算法的可行基础。。。

Part.III 生日悖论???

先看一下这个问题

考虑从[1,1000][1,1000][1,1000]中选出一个数,要求恰好是424242,显然这个概率为11000\frac{1}{1000}10001?

那么我们要选两个数i,ji,ji,j使得i?j=42i-j=42i?j=42呢?算一下就可以发现这个概率大概是1500\frac{1}{500}5001?

那么我们要选出kkk个数x1,x2,…,xkx_1,x_2,\ldots,x_kx1?,x2?,,xk?使得xi?xj=42x_i-x_j=42xi??xj?=42呢?

写个程序玩一下就会发现:选了100个数的概率居然非常接近111了。

这个被称为生日悖论

Part.IV 如何用生日悖论

多打打表就发现当我们选取k=Nk=\sqrt{N}k=N ?个数时,成功的概率已经超过了50%50\%50%

换句话说,我们已经将找到质因数的概率从1N\frac{1}{N}N1?提到了1N\sqrt{\frac{1}{N}}N1? ?。这意味着我们对于一个101010^{10}1010级别的数,我们只需找到k=105k=10^5k=105个数。

但是,另一个不幸的消息是我们需要用k2k^2k2次除法和比较。 这不是又炸了QAQ…

然而我们可以这样来做:对于一个xix_ixi?,再随机构造一个xyx_yxy?,求gcd?(∣xi?xy∣,N)\gcd(|x_i-x_y|,N)gcd(xi??xy?,N),可以发现,当这个gcd?(∣xi?xy∣,N)\gcd(|x_i-x_y|,N)gcd(xi??xy?,N)不等于111时,我们就找到了一个质因子。

但直接存下这kkk个数似乎不太可能存进去。

Part.V 总算到了Pollard-Rho算法

Pollard-Rho算法的实现只存下了两个数。

我们一直往下生成两个数,每次只比较和检查这两个数,然后直到找到我们想要的数。

我们使用一个伪随机数算法来生成下一个数,设当前数数xxx,则我们的下一个数就是x2+amodNx^2+a\mod Nx2+amodN

P.S.关于常数aaa,可以指定,也可以rand()一个。

Part.VI 还有问题???

如果你运气好的话,你总会遇到一些数,他们会让Pollard-Rho算法进入死循环。

如何解决?

有一种方法是开一个数组,然后不断标记。

然而数字很大的时候就要炸。。。

考虑这样一个问题:

你在操场上走,然而操场周围的景物都一样,那么你如何知道你已经走了一圈?

一个有效的方法是再找一个人 不要问我怎么找到的 ,然后让他以你的两倍的速度向前走,当你和那个人相遇时,你就走完了一圈。

这样的话,我们就可以用两个变量x,yx,yx,y,初始值相同,每次进入循环时,令x=f(x),y=f(f(y))x=f(x),y=f(f(y))x=f(x),y=f(f(y)),每次只需检查gcd?(∣x?y∣,N)\gcd(|x-y|,N)gcd(x?y,N)即可。

模版

typedef long long ll;
const int pri[]={
    2,3,5,7,11,13,17,19,23,29};inline ll QuickMul(ll a,ll b,ll mod) {
    a%=mod,b%=mod;ll ret=0;while(b) {
    if(b&1)ret=(ret+a)%mod;a=(a+a)%mod;b>>=1;}return ret;
}
inline ll GCD(ll x,ll y) {
    return y==0?x:GCD(y,x%y);}
inline ll ABS(ll x) {
    return x<0?-x:x;}ll PollardRho(ll n) {
    for(int i=0;i<10;i++)if(n%pri[i]==0)return pri[i];ll x=(1LL*rand()*rand()%(n-2))+2;ll y=x;ll c=(1LL*rand()*rand()%(n-1))+1;ll d=1;while(d==1) {
    x=(QuickMul(x,x,n)+c+n)%n;y=(QuickMul(y,y,n)+c+n)%n;y=(QuickMul(y,y,n)+c+n)%n;d=GCD(ABS(x-y),n);if(d==n)return n;}return d;
}