暴力算法

这是一个暴力递归分解质因数的伪代码

void rp(int n) {if (n 是 质数) n 是 rp 的一个质因子, return;枚举找到 n 的一个非 1 因子 p;rp(p), rp (n / p);
}

其中判断质数可以用 Miller Rabin。该暴力复杂度的瓶颈是找因子,如果给定的 nnn 是一个大质数如 109+710^9+7109+7,那么枚举的复杂度就一飞冲天了。

生日悖论

假设 n=109+7n=10^9+7n=109+7,只有一个非 111 因子。

如果一个班上有 kkk 个人,那么有两个生日相同的人的概率是多少?

答案为 P(k)=1−∏i=1k365−i+1365P(k)= 1 - \prod_{i=1}^k \frac{365-i+1}{365}P(k)=1−∏i=1k​365365−i+1​。当 k=48k=48k=48 时,P(k)≈0.960598P(k) \approx 0.960598P(k)≈0.960598。

那么我们可以用生日悖论来进行找因子

我们在 [1,n][1,n][1,n] 中随便选一个数,那么他是因子的概率为 1n\frac{1}{n}n1​。

我们在 [1,n][1,n][1,n] 中随便选两个数 aaa 和 bbb,那么 ∣a−b∣|a-b|∣a−b∣ 是因子的概率为 2n\frac{2}{n}n2​。这相当于 a=b+na = b + na=b+n 和 a=b−na = b - na=b−n 两个情况。

我们在 [1,n][1,n][1,n] 中随便选 kkk 个数 a1∼aka_1 \sim a_ka1​∼ak​,对于任意两个数 aia_iai​ 和 aja_jaj​,如果有 gcd⁡(∣ai−aj∣,n)≠1\gcd(|a_i-a_j|,n) \not =1gcd(∣ai​−aj​∣,n)​=1,意味着我们找到了 nnn 的一个因子,它的概率为 2×k(k−1)2n\frac{2 \times \frac{k(k-1)}{2}}{n}n2×2k(k−1)​​。当 k=nk = \sqrt{n}k=n​ 时,概率已经接近百分之百了。但如果求这 kkk 个数的两两差,复杂度又回到了 O(n)O(n)O(n),显然不可行。

伪随机函数

fi=(fi−12+c)modnf_i = (f_{i-1^2+c}) \bmod n fi​=(fi−12+c​)modn

其中 ccc 可以是 [1,n][1,n][1,n] 内的任意一个数,我们任取一个数作为 f1f_1f1​,然后剩下的数列根据这个函数随机生成。可以发现在modp\bmod pmodp 下,一旦有两个数相同,那么这个数列会陷入循环节,陷入循环节前的长度期望是 n\sqrt nn​ 的。

那么这个函数的优点是什么呢?如果 gcd⁡(fi−fj,n)≠1\gcd(f_i-f_j,n) \not=1gcd(fi​−fj​,n)​=1,那么有 fi+1−fj+1=(fi−fj)(fi+fj)f_{i+1}-f_{j+1}=(f_i-f_j)(f_i+f_j)fi+1​−fj+1​=(fi​−fj​)(fi​+fj​),gcd⁡(fi+1,fj+1,p)≠1\gcd(f_{i+1},f_{j+1},p) \not = 1gcd(fi+1​,fj+1​,p)​=1。

算法实现

int f(int a, int n, int c)
{return (1ll * a * a + c) % n;
}
void factor(int n) {if (miller_rabin(n)) {z[++cnt] = n; return ;}int g = n;while (g == n) g = pollard_rho(n); //若 g == n,则这次 pollard_rho 的 rp 不好,没有找到因子factor(g), factorn(n / g);
}

现在我们要实现 Pollard_Rho 的主体了。Pollard_Rho 的思想就是在随机数列上选一些数与 nnn 求最大公约数,如果遇到环则跳出。现在有一个效率低下的双指针法。

int pollard_rho(int n) {int c = rand() % (n - 1) + 1;int v1 = c;int v2 = f(v1,n,c);while (v1 != v2) {int g = gcd(n, abs(v1 - v2));if (g > 1) return g;v1 = f(v1,n,c); //v1 跳一次v2 = f(v2,n,c); v2 = f(v2,n,c); //v2 跳两次}return n;
}

我们知道求 gcd⁡\gcdgcd 的复杂度是 log⁡\loglog 的,求太多次会使得代码时间复杂度低下。我们不如用倍增的思想,让 v2v2v2 在一个范围中跳上好几次,把这几次的结果一起求一下 gcd⁡\gcdgcd。这样看上去比较玄学,但是对复杂度却如虎添翼。

int pollard_rho(int n) {int c = rand() % (n - 1) + 1;int v1 = 0;for (int s = 1, t = 2; s <<= 1, t <<= 1) {int v2 = v1, mul = 1;for (int i = s, step = 1; i < t; i++, step++) {v2 = f(v2, n, c);mul = 1ll * mul * abs(v1 - v2) % n;if (step == 128) {step = 0;int v = gcd(mul, n);if (v > 1) return v;}}int v = gcd(mul, n);if (v > 1) return v;v1 = v2;}
}

现在时间复杂度得到了大大提升,当然 Pollard_rho 还有许多更优的实现方式,甚至有许多比 Pollard_rho 快了不知道几十倍的质因数分解算法,但在算法竞赛中此法足矣。

Pollard Rho 质因数分解相关推荐

  1. 大整数分解——Pollard Rho算法

    延续上一篇,这次来讲一讲大整数分解算法的应用. 要解决的问题很简单,对一个整数进行分解质因数. 首先还是效率非常低的暴力算法,相信大家都会,不多提. 和上次一样,当数达到非常大的时候,分解将变得非常困 ...

  2. 素数判定质因数分解(数论)(Miller Rabin)(Pollard Rho)

    太玄学了! 我真的被概率的魅力折服了.此前我认为1便是1,0.9999999999-便是0.9999999999-. 但实际上它们有着千丝万缕的关系. 试想,如果一件事发生的概率是0.99999999 ...

  3. C++实现质因数分解

    质数(prime number)又称素数,有无限个.一个大于1的自然数,除了1和它本身外,不能被其他自然数整除(除0以外)的数称之为素数(质数):否则称为合数.根据算术基本定理,每一个比1大的整数,要 ...

  4. c语言素数筛法与分解素因数,质因数分解及代码:

    计算方法 短除法 求一个数分解质因数,要从最小的质数除起,一直除到结果为质数为止.分解质因数的算式的叫短除法,和除法的性质差不多,还可以用来求多个个数的公因式: 求最大公因数的一种方法,也可用来求最小 ...

  5. 质因数分解及算法实现

    每个合数都可以写成几个质数相乘的形式,这几个质数就都叫做这个合数的质因数.如果一个质数是某个数的因数,那么就说这个质数是这个数的质因数.而这个因数一定是一个质数. 定义 质因数(或质因子)在数论里是指 ...

  6. Python 大数的质因数分解

    肝了一天总算把大数质因数分解搞定了,这篇文章主要涉及了 Pollard rho 算法和试除法 我用了若干个质数的平方来对比这两个算法的性能,发现: 7e5 以上的数用 Pollard rho 算法更快 ...

  7. 64位以内Rabin-Miller 强伪素数测试和Pollard rho 因数分解解析

    在求解POJ1811题Prime Test中应用到的两个重要算法是Rabin-Miller强伪素数测试和Pollard r因数分解算法.前者可以在的时间内以很高的成功概率判断一个整数是否是素数.后者可 ...

  8. 整数的素因子分解:Pollard rho method

    参考: 1.CLRS<算法导论> 2.http://www.csh.rit.edu/~pat/math/quickies/rho/#algorithm Pollard rho方法是随机算法 ...

  9. Poj 1811 Prime Test 素数测试 Miller-Rabin 与 整数的因子分解 Pollard rho

    随机化算法,想尝试自己写一下,最后还是变成了抄代码... 代码参考了:POJ 1811 Prime Test(大素数判断和素因子分解) - kuangbin - 博客园 学习链接: Miller-Ra ...

  10. 素数、最大公约数、最下公倍数、质因数分解

    2013-08-18 11:20:43 素数.最大公约数.最下公倍数.质因数分解都是与素数相关的,解决了素数的问题,其他的都可以此为基础求解. 小结: 求1到n之间的素数的基本方法是通过遍历2到sqr ...

最新文章

  1. jetty9请求form表单太小限制
  2. Linux——33条小技巧
  3. PostgreSQL 10.1 手册_部分 II. SQL 语言_第 12 章 全文搜索_12.9. GIN 和 GiST 索引类型
  4. GitHub使用教程for Eclipse
  5. centos 5.5中如何由一般用户切换为root用户
  6. 天气预报HTML代码
  7. 如何向开源软件贡献自己的力量
  8. Bootstrap布局
  9. pytorch torch.nn.MSELoss
  10. mysql 是如何利用索引的_10 分钟搞明白 MySQL 是如何利用索引的!
  11. C++之父谈C++:一天之内你就能学会出色使用C++
  12. 去除本机利用ssh协议登陆远程机器的痕迹
  13. 【转】Data truncation: Truncated incorrect DOUBLE value:Mysql Update时
  14. 02WCF初识:ServiceEndpoint
  15. 精通Javascript+jQuery视频下载地址
  16. 防止SQL注入的五种方法
  17. 正则表达式的进阶用法——预查与分组
  18. 如何设置文件的默认打开方式
  19. 抠图:基于单个原色通道
  20. 数据传输/文件传输:两台电脑怎么传文件?

热门文章

  1. Todd Lammle's CCNA IOS Commands Survival Guide
  2. chrome 设置保护眼睛颜色
  3. 织梦个人网站即时到账支付插件
  4. 怎么用c语言让电脑定时开关机,电脑定时开关机,教您怎么设置电脑定时开关机...
  5. oracle中的than,range分区values less than,代表的是小于等于还是小于啊。
  6. hibernate_Hibernate记录:常见问题的提示和解决方案
  7. 科学研究设计六:有效性威胁
  8. 程序员怎样更优雅的接私活赚外快
  9. java 如何清除临时文件_如何删除Java中的临时文件?
  10. 《Qt on Android核心编程》介绍