复习整理一下PollardRho的相关知识把,刚好密码,acm都要用

Miller Rabin素性检验

Miller Rabin素性检验是一种素数判定的法则,由CMU的教授Miller首次提出,并由希大的Rabin教授作出修改,变成了今天竞赛人广泛使用的一种算法,故称Miller Rabin素性检验。
该算法本质上是一种随机化算法,能在O(klog3n)O(klog^3n)O(klog3n) 的时间复杂度下快速判断出一个数是否是素数,但具有一定的错误概率。不过在一定数据范围内,通过一些技巧可以使其不出错。

Fermat素性检验

由费马小定理我们得知:对任意素数ppp和与其互质的整数aaa ,ap−1≡1(modp)a^{p-1}\equiv1(mod\ p)ap−1≡1(mod p) 。
随机选取一小于ppp的整数aaa,若ap−1≡1(modp)a^{p-1}\equiv1(mod\ p)ap−1≡1(mod p) ,则是否可以证明ppp是一素数?
答案是不行,因为存在一些合数满足这个等式,这些合数被称为费马伪素数,例如最小的费马伪素数为341。
通过多选取几个底数,我们很大程度上降低了错误的概率,比如341就成功被筛去了。但仍旧存在极少的一些合数,即便遍历[2,p−1][2,p-1][2,p−1]的每一个数字作为底数,也无法筛去。
这样的合数被称为卡迈克尔数,在一亿内有255个,最小的卡迈克尔数为561。
若nnn为卡迈克尔数,则2n−12^n-12n−1也是卡迈克尔数,故其个数是无穷的。

Miller Rabin素性检验

单单费马小定理行不通,所以我们考虑引入新的定理来提高检测的正确性。
二次探测定理 :对于质数ppp,若x2≡1(modp)x^2\equiv1(mod\ p)x2≡1(mod p),则小于ppp的解只有两个,x1=1,x2=p−1x_1=1,x_2=p-1x1​=1,x2​=p−1。
由Fermat检测得到ap−1≡1(modp)a^{p-1}\equiv1(mod\ p)ap−1≡1(mod p),且 p−1p-1p−1为偶数(否则ppp为偶数直接特判筛掉),则 ap−1a^{p-1}ap−1就相当于x2x^2x2。
也就是说,我们可以将p−1=u×2t(u为奇数)p-1=u\times2^t(u为奇数)p−1=u×2t(u为奇数) ,对au,au×2,au×22…a^u,a^{u\times2},a^{u\times2^2}…au,au×2,au×22… 这一系列数进行检验,他们的解要么全是1,要么出现p−1p-1p−1后全是1(之前不能有1),否则就不是素数。当然,要注意p−1p-1p−1 不能出现在最后一个数,否则就连费马小定理都不满足了。
还要注意这过程中不能产生ppp的倍数。
总结一下,该检验的思路是:

1)先特判筛掉3以下的数与偶数。
2)将待检验数nnn,n−1n-1n−1化为u×2tu\times2^tu×2t 。
3)选取多个底数,分别对au,au×2,au×22…a^u,a^{u\times2},a^{u\times2^2}…au,au×2,au×22… 进行检验,判断其解是否全为1,或在非最后一个数的情况下出现p−1p-1p−1。
4)如果都满足,我们就认为这个数是素数。

代码解决

那么思路的问题解决了,接下来考虑代码的实现。
首先是底数的选取:Miller Rabin检测依旧有错误的概率,虽然微乎其微,但我们显然不想接受。但通过选取适合的底数,可以避免这一情况的发生。
在int范围内,选取{2,7,61}\{2,7,61\}{2,7,61} 三个数作为底数可以保证100%正确。
在long long范围内,{2,325,9375,28178,450775,9780504,1795265022}\{2,325,9375,28178,450775,9780504,1795265022\}{2,325,9375,28178,450775,9780504,1795265022} 七个数作为底数可保证100%正确。
故正常情况下时间复杂度最多为O(log3n)O(log^3n)O(log3n) ,常数为7。
其次,由于这个过程中可能要计算long long型变量的平方,所以要考虑数据溢出的问题,解决方案是快速乘。
这里提供一个O(1) 的快速乘,原理是用溢出来解决溢出。

inline ll qmul(ll x,ll y,ll mod){return (x*y-(ll)((ld)x/mod*y)*mod+mod)%mod;
}

总参考代码:

inline ll qmul(ll x, ll y, ll mod){return (x*y-(ll)((ld)x/mod*y)*mod+mod)%mod;
}
ll qpow(ll a, ll n, ll mod)/{ll res=1;while(n){if(n&1) res = qmul(res,a,mod);a = qmul(a,a,mod);n >>= 1;}return res;
}bool Miller_Rabin(ll n){if(n<3 || n&1==0) return n==2;ll u=n-1, t=0;while(u&1 == 0) u/=2, t++;ll ud[]={2,325,9375,28178,450775,9780504,1795265022};for(ll a:ud){ll v=qpow(a, u, n);if(v==1 || v==n-1 || v==0) continue;for(int j=1;j<=t;j++){v=qmul(v,v,n);if(v==n-1 && j!=t){v=1;break;}if(v==1) return 0;}if(v!=1) return 0;//Fermat检验}return 1;
}

Floyd判圈算法(龟兔赛跑算法)

算法简述

Floyd判圈算法(Floyd Cycle Detection Algorithm),又称龟兔赛跑算法(Tortoise and Hare Algorithm),是一个可以在有限状态机、迭代函数或者链表上判断是否存在环,以及判断环的起点与长度的算法。

基本思路

在某种关系下,顶点 i 到 k 拓扑有序,顶点 k 到 j 也是相同的顺序,那么 i 和 j 也存在这个顺序。要是某一个顶点出现了自己到自己的环,那么图中就有环,但是这种方法复杂度高一些,没有检测顶点出度或者DFS的方法快,但是非常简单。

(1)判断是否有环
龟兔解法的基本思想可以用我们跑步的例子来解释,如果两个人同时出发,如果赛道有环,那么快的一方总能追上慢的一方。进一步想,追上时快的一方肯定比慢的一方多跑了几圈,即多跑的路的长度是圈的长度的倍数。
基于上面的想法,Floyd用两个指针,一个慢指针(龟)每次前进一步,快指针(兔)指针每次前进两步(两步或多步效果时等价的,只要一个比另一个快就行)。如果两者在链表头以外的某一点相遇,那么说明链表有环,否则,如果(快指针)到达了链表的结尾,那么说明没环。
(2)求环的长度
相遇的时候,一定已经在环上了,然后两个人只要再次在环上接着跑,再次相遇的时候(也就是所谓的套圈),跑的快的那个人就比跑的慢的人整整多跑了一圈,所以环的长度也就出来了。
(3)如何确定环的起点
环的检测用上述原理,接下来我们来看一下如何确定环的起点,这也是Floyd解法的第二部分。方法是将其中一个指针移到链表起点,两者同时移动,每次移动一步,那么两者相遇的地方就是环的起点。

解析:

首先假设第一次相遇的时候慢指针走过的节点个数为i,设链表头部到环的起点的长度为m,环的长度为n,相遇的位置与起点与起点位置距离为k。
于是有:i=m+a∗n+ki=m+a*n+ki=m+a∗n+k,其中a为慢指针走的圈数。
因为快指针的速度是慢指针的2倍,于是又可以得到另一个式子:2∗i=m+b∗n+k2*i=m+b*n+k2∗i=m+b∗n+k,其中b为快指针走的圈数。
两式相减得:i=(b−a)∗ni=(b-a)*ni=(b−a)∗n,也就是说i是圈长的整数倍。
这是将其中一个节点放在起点,然后同时向前走m步时,此时从头部走的指针在m位置。而从相遇位置开始走的指针应该在距离起点i+m,i为圈长整数倍,则该指针也应该在距离起点为m的位置,即环的起点。

PollardRho 算法:

PollardRho 是一个很神奇的算法,用于在 O(n1/4)O(n^{1/4})O(n1/4) 的期望时间复杂度内计算合数n的某个非平凡因子(除了1和它本身以外能整除它的数)。事书上给出的复杂度是 O(p)O(\sqrt{p})O(p​) , p 是 n 的某个最小因子,满足 p 与 n/p 互质。虽然是随机的,但 PollardRho 算法在实际环境中运行的相当不错,不会被卡。

问题模型:

给一个数 n ,你需要快速求出它的一个非平凡因子。
对于一个比较小的数(n≤109n≤10^9n≤109),我们直接暴力枚举质因子就行但太大了我们就必须考虑一下随机算法了。
对于一个非常大的合数 n≤1018n≤10^{18}n≤1018 (如果可能是质数,我们可以用Miller Rabin判一下)我们要求 n 的某一个非平凡因子,如果 n 的质因子很多(就是约数很多)我们也可以暴力随机弄一弄,但如果是一些(像由两个差不多的而且很大的质数乘得的n)它的非平凡因子就只有两个而且 n 本身还很大,此时如果我们随机的话复杂度 O(n)O(\sqrt{n})O(n​) ,这个太难接受了,所以我们想办法优化一下。

1.求 gcd

直接随机求 n 的某一个约数复杂度很高,可以考虑求一下 gcd 。因为我们可以保证一个合数它绝对存在一个质因子小于 n\sqrt{n}n​ ,所以在 n 就存在至少n\sqrt{n}n​ 个数与 n 有大于一的公约数。于是我们随机的可能性就提高到了 O(nlgn)O(\sqrt{n}lgn)O(n​lgn) 。

2.生日悖论

生日悖论其实不是一个逻辑悖论,只是与很多人的第一感觉不符罢了。它可以表述为: 一个房间里有23个人,则他们中有两人生日相同的概率超过一半 (不考虑闰年)。其实这在数学上很好证明,即 365365×364365×363365×⋯×365−22365<12\frac{365}{365}\times\frac{364}{365}\times\frac{363}{365}\times\cdots\times\frac{365-22}{365}<\frac12365365​×365364​×365363​×⋯×365365−22​<21​ 。
生日悖论启示我们,如果我们不断在某个范围内生成随机整数,很快便会生成到重复的数,期望大约在根号级别。精确地说,对于一个[1,N][1,N][1,N]内整数的理想随机数生成器,生成序列中第一个重复数字前期望有πN2\sqrt{\frac{πN}{2}}2πN​​个数
应用到原题上,即是对于最坏情形n=p2n=p^2n=p2 ,如果我们不断在[1,n−1][1,n-1][1,n−1] 间生成随机数,那么期望在生成大约p=n14\sqrt p=n^{\frac14}p​=n41​ 个数后,可以出现两个在模ppp下相同的数(注意[1,n−1][1,n-1][1,n−1] 间的随机数模ppp 大致是[0,p−1][0,p-1][0,p−1]间的随机数)。那么这两个数的差的绝对值 ddd ,就一定满足d≡0(modp)d\equiv0\pmod pd≡0(modp) ,于是也满足gcd⁡(d,n)>1\gcd(d,n)>1gcd(d,n)>1 。
但这件事意义并没有那么大。正如虽然生日悖论是正确的,但你不一定能在班上遇到和自己生日相同的人,因为这个高概率是在两两比较下才成立的。对这n14n^{\frac14}n41​ 个数两两进行验证,复杂度立刻退回到 O(nlg⁡n)O(\sqrt n\lg n)O(n​lgn) ,并没有什么进步。所以我们需要一些技巧。

3.伪随机数序列

Pollard使用一种特别的伪随机数生成器来生成 [0,N−1][0,N-1][0,N−1] 间的伪随机数序列:设序列第一个数为 xxx ,f(x):=(x2+c)modNf(x):=(x^2+c)\bmod Nf(x):=(x2+c)modN ,则x,f(x),f(f(x)),⋯x,f(x),f(f(x)),\cdotsx,f(x),f(f(x)),⋯为一个伪随机数序列。
以下为x=0,c=24,N=9409x=0,c=24,N=9409x=0,c=24,N=9409时,生成的前100个数:

不过如果稍微换一下参数,把NNN换成940094009400的话……

其实这是显然的,因为每个数都是由前一个数决定的,可以生成的数又是有限的,那么迟早会进入 循环 。当然,这个循环很可能是 混循环 ,所以生成的序列常常形成这样的ρ形,这也是为什么Pollard把这个算法命名为rho算法:
[图片]
我们在这里使用 Floyd判环算法 (也叫 龟兔赛跑算法 ),设置两个变量t,rt,rt,r ,每次判断是否有 gcd⁡(∣t−r∣,N)>1\gcd(|t-r|,N)>1gcd(∣t−r∣,N)>1 ,如果没有,就令t=f(t)t=f(t)t=f(t) ,r=f(f(r))r=f(f(r))r=f(f(r)) 。因为 r 跑得更快,如果没有找到答案,最终会与 t 在环上相遇,这时退出,换一个 c 重新生成伪随机数。
那么,这有什么好处呢?其实,这个伪随机数生成器生成的数具有一个性质。注意到,如果∣i−j∣≡0(modp)|i-j|\equiv 0\pmod p∣i−j∣≡0(modp) ,那么一定有∣f(i)−f(j)∣=∣i2−j2∣=∣i−j∣⋅∣i+j∣≡0(modp)|f(i)-f(j)|=|i^2-j^2|=|i-j|\cdot|i+j|\equiv0\pmod p∣f(i)−f(j)∣=∣i2−j2∣=∣i−j∣⋅∣i+j∣≡0(modp) 。由此可得, 只要环上距离为 d 的两个数满足条件,那么所有距离为 d 的数都满足条件 。在Floyd判环的过程中,每次移动都相当于在检查一个新的距离 d ,这样就不需要进行两两比较了。
这个算法的复杂度依赖于这个伪随机数生成器的随机程度,还没有被严格证明。如果它是足够随机的,那么期望复杂度显然是O(n14log⁡n)O(n^{\frac14}\log n)O(n41​logn)。

4.判环

这种随机生成方法虽然优秀,但也有一些需要注意之处,比如有可能会生成一个环,并不断在这个环上生成以前生成过一次的数,所以我们必须写点东西来判环:

  1. 我们可以让y根据生成公式以比x快两倍的速度向后生成,这样当y再次与x相等时,x一定跑完了一个圈且没重复跑多少!
  2. 我们可以用倍增的思想,让y记住x的位置,然后x再跑当前跑过次数的一倍的次数。
inline ll rho(ll n){ll x, y, c, g, i=0, j=1;x=y=rand(); c=rand();while(i++){x=(qmul(x,x,n)+c)%n;if(x==y)break;g=gcd(abs(y-x),n);if(g>1)return g;if(i==j)y=x,j<<=1;}
}

PollardRho 算法的二次优化:

我们发现其实 PollardRho 算法其实复杂度不止 O(n1/4)O(n^{1/4})O(n1/4) ,它每一次操作都会求一次 gcd ,所以复杂度会变成 O(n1/4×log)O(n^{1/4}×log)O(n1/4×log) 我们发现这个 log 实在有些拖慢速度。于是一个很奇妙的优化诞生了!
二次优化 :我们将若干个 (y−x) 乘到一起,如果我的若干个 (y−x) 中有一个与n有公约数,最后的结果定然也会含有这个公约数!所以我们完全可以多算几次 (y−x) 的乘积在来求 gcd (一般连续算127次再求一次gcd)
对原算法的一些影响: 任何一个优化都是要考虑其对原算法的影响的。这个优化也会有一些影响:首先,因为我们会等大概127次后再去gcd,然而我们有可能在生成时碰上一个环,我们有可能还没生成127次就跳出这个环了,这样就无法得出答案;其次,我们的 PollardRho 算法实在太玄学优秀了,可能我们跑127次之后,所有 (y−x) 的乘积就变成了n的倍数(模 n 意义下得到 0 ),所以我们不能完全就只呆板的等127次在取模。
我们可以用倍增,分别在生成(1次,2次,4次,8次,16次,32次…)之后进行 gcd !这样就可以完全避免上述的两个影响

inline ll rho(ll p){ll x, y, z, c, g, i;while(1){y = x = rand()%p;z = 1;c = rand()%p;i=0, j=1;while(i++){x = ((x*x%p)+c)%p;z = z*abs(y-x)%p;if(x==y || !z)break;if(i%127==0 || i&(i-1)==1){//分别在i=2^n 和 i%127==0时求gcdg = __gcd(z,p);if(g > 1)return g;if(i == j)y=x, j<<=1;}}}
}

总参考代码

inline ll qmul(ll x,ll y,ll mod){return (x*y-(ll)((long double)x/mod*y)*mod+mod)%mod;
}ll qpow(ll a,ll n,ll mod)/{ll res=1;while(n){if(n&1) res=qmul(res,a,mod);a=qmul(a,a,mod);n>>=1;}return res;
}bool Miller_Rabin(ll n){if(n<3 || n&1==0) return n==2;ll u=n-1, t=0;while(u&1 == 0) u/=2, t++;ll ud[]={2,325,9375,28178,450775,9780504,1795265022};for(ll a:ud){ll v=qpow(a,u,n);if(v==1 || v==n-1 || v==0) continue;for(int j=1; j<=t; j++){v = qmul(v,v,n);if(v==n-1 && j!=t){ v=1; break; }if(v == 1) return 0;}if(v != 1) return 0;//Fermat检验}return 1;
}inline ll rho(ll p){ll x, y, z, c, g, i, j;if(Miller_Rabin(p)) return p;while(1){y = x = rand()%p;c = rand()%p + 2;i = 0, j = 1, z = 1;while(i++){x = (qmul(x,x,p) + c) % p;z=qmul(z, abs(y-x), p);if(x==y || !z) break;if(i%127==0 || i&(i-1)==1){//分别在i=2^n 和 i%127==0时求gcdg = __gcd(z,p);if(g > 1) return g;if(i == j) y=x, j<<=1;}}}
}

pollard_rho相关推荐

  1. BZOJ 4802: 欧拉函数(大数因数分解算法 Pollard_rho 和素数测试算法 Miller_Rabin)

    Description 已知N,求phi(N) Input 正整数N.N<=10^18 Output 输出phi(N) Sample Input 8 Sample Output 4 Soluti ...

  2. 算个欧拉函数给大家助助兴(米勒拉宾(判断素数)+Pollard_rho(求一个大数的因子 ))

    这篇博客讲的很好: https://www.cnblogs.com/ZERO-/p/9302169.html 题目描述 木南有一天学习了欧拉函数,知道了对正整数n,欧拉函数是小于n的正整数中与n互质的 ...

  3. 【BZOJ-4522】密钥破解 数论 + 模拟 ( Pollard_Rho分解 + Exgcd求逆元 + 快速幂 + 快速乘)...

    4522: [Cqoi2016]密钥破解 Time Limit: 10 Sec  Memory Limit: 512 MB Submit: 290  Solved: 148 [Submit][Stat ...

  4. Pollard_rho算法+Miller_Rabin算法(大素数的判断与素因子分解)(模板)

    const int S=20;//随机算法判定次数,S越大,判错概率越小 //计算 (a*b)%c. a,b都是long long的数,直接相乘可能溢出的 // a,b,c <2^63 LL m ...

  5. 基于线性筛的Pollard_rho 因数分解算法【例题】

    目录 题目 输入 输出 思路 参考文章 代码 题目 输入 2 1 2 1 1000000 输出 1 3626619 思路 算[1,1e6]区间里面每个数的质因子次方数的和.再简化一点,给你一个大数,将 ...

  6. poj1811(pollard_rho模板)

    题目链接: http://poj.org/problem?id=1811 题意: 判断一个数 n (2 <= n < 2^54)是否为质数, 是的话输出 "Prime" ...

  7. Pollard_rho大数质因数分解+拉格朗日四平方和定理(bzoj 2904: 平方和)

    2904: 平方和 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 160  Solved: 73 [Submit][Status][Discuss] ...

  8. 【BZOJ4522】密匙破解(Pollard_rho)

    [BZOJ4522]密匙破解(Pollard_rho) 题面 BZOJ 洛谷 题解 还是\(Pollard\_rho\)的模板题. 呜... #include<iostream> #inc ...

  9. upc组队赛16 GCDLCM 【Pollard_Rho大数质因数分解】

    GCDLCM 题目链接 题目描述 In FZU ACM team, BroterJ and Silchen are good friends, and they often play some int ...

  10. Miller_Rabin和Pollard_Rho算法

    Miller_Rabin和Pollard_Rho算法 SemiWaker 一直觉得随机化不稳定就没学. Miller_Rabin 目的:判质数. 当然,不是随机一个数然后去模,随机一个数去模正确率太低 ...

最新文章

  1. ubuntu 14.10 使用fcitx输入法
  2. 都说Djnago框架重,那就让哥用15行代码写个django web程序!
  3. Dubbo搭建HelloWorld-搭建服务提供者与服务消费者并完成远程调用(附代码下载)
  4. python mysql批量更新_Python批量删除mysql中千万级大量数据的脚本分享
  5. linux system更好方法,Linux将程序添加到服务的方法(通用【但最好还是用systemd】)...
  6. python开三次方根函数_Python 中,给 -8 开三次方根出来的是一个虚数,而不是 -2,这怎么办?...
  7. 程序员如何保护好自己的发际线
  8. Open Source Drives IOT From Device to Edge
  9. 163邮箱链接服务器失败是怎么回事,outlook邮箱添加163邮箱账户失败该怎么办?...
  10. python爬取instom图片_用python爬虫保存instagram图片
  11. SpringBoot+Layui就业信息管理系统
  12. Unity 截屏时,安卓和编辑器下,Rect的坑
  13. 美国人初学编程代码之三
  14. 2022年 CSP-J1 CSP-S1 初赛 如何进行复习 如何做题
  15. 网络流-割的概念以及定理
  16. 腾讯实时音视频SDK[一]:业务和场景
  17. java如何接收十六进制_JAVA十六进制数据接收与传输
  18. WIN7系统添加python的pip环境变量
  19. xshell下载日志命令_Xshell日志的保存方法
  20. mybats实操-前期入门写法分析,SqlSessionFactory 获取SqlSession, 系统核心配置文件 mybatis-config.xml,SQL映射XML文件,MyBatis缓存

热门文章

  1. 无向简单图怎么判断_无向图基础
  2. 下面最难防范的网络攻击是计算机病毒,求大神们帮帮忙,拜托了,在这先谢过了。...
  3. 筱筱看博客(git 冲突解决)
  4. 《大话计算机》动图一则展示
  5. 推荐一个非常好的IOS编程技巧网站
  6. [爬虫实践blog]之——GET公众号封面图
  7. 100本最棒的web前端图书推荐
  8. PS如何查看所选图层的实际像素?
  9. 到底多大并发才算高并发?一文带你全面认识高并发!
  10. php jq 提交表单验证,jQuery EasyUI 表单 – 表单验证 | 菜鸟教程