Pollard's Rho

  • 质数的判定
    • 试除法
    • Fermat 素性测试
    • Miller-Rabin 素性测试
  • 查找因数
    • 还是试除法
    • Pollard's Rho
  • 分解质因数

  随便写写,不喜勿喷。


质数

Prime


  对于整数 nnn,若 n≠−1,0,1n \neq -1,0,1n​=−1,0,1,且 nnn 除去显然因数(±n(\pm n(±n、±1)\pm 1)±1) 外没有其他因数,则我们称 nnn 为质数。


质数的判定

Primality test


  又称 素性测试,

  是一种判定某个数是否为质数的方法。

  素性测试有两种,

  1.确定性测试 \ 确定型算法
  2.概率性测试 \ 随机型算法

  确定性测试可绝对确定一个数是否为质数,概率性测试则有较小的概率错误的将合数判断为质数,因此,通过了概率性测试的数字被称为 可能质数,通过了概率性测试的合数被称为 伪质数,常见的有费马伪质数。

  下面将简单带过一下,暴力测试(试除法),

  引入费马小定理,以及两个基于费马小定理的随机型算法 费马素性测试、Miller\mathrm{Miller}Miller-Rabin\mathrm{Rabin}Rabin 素性测试。


试除法

Trial Division


  一个数的因子总是成对出现的,因此对于正整数 nnn,它的最小因子不会大于 n\sqrt nn​,

  若 2∼n2 \sim \sqrt n2∼n​ 中不存在一个整数 kkk,使得 k∣nk \mid nk∣n,那么我们就可以确定的认为 nnn 就是质数。

bool is_prime(int n) {for (int i = 2; i <= sqrt(n); ++i)if (n % i == 0) return 0;return 1;
}

费马小定理

Fermat’s little theorem


  若 ppp 是质数,aaa 为整数且 aaa 不是 ppp 的倍数,则有:ap−1≡1(modp)a^{p-1}\equiv1 \pmod pap−1≡1(modp)

  费马小定理也是欧拉定理的特殊形式。


Fermat 素性测试


  费马测试是最为简单的概率性测试,

  其思想是,不断的在 2∼n−12 \sim n-12∼n−1 中,选择一个 aaa 作为基,判断对于整数 aaa,是否都有 ap−1≡1(modp)a^{p-1}\equiv1 \pmod pap−1≡1(modp)。

int mod_pow(int a, int b, int p) {int pow = 1;while (b) {if (b & 1) pow = pow * a % p;a = a * a % p;b >>= 1;}return pow;
}int test_time = 50;bool is_prime(int n) {for (int i = 0; i < test_time; ++i)if (mod_pow(1 + rand() % (n - 1), n - 1, n) != 1) return 0;return 1;
}

  在上面程序中,随机选择了 aaa,极大程度上避免了错误判定的发生,但有一类数无法被费马测试准确判断,

  这类数被称为 卡迈克尔数,也被称为 费马伪素数,

  其定义为对于合数 nnn,对于任意整数 aaa,gcd⁡(a,n)=1\gcd (a,n)=1gcd(a,n)=1 ,都有 an−1≡1(modn)a^{n-1} \equiv 1 \pmod nan−1≡1(modn) 成立,则称这样的数为 卡迈克尔数,卡迈克尔数 有无穷多个,最小的 卡迈克尔数 是 561561561,这种数的存在也佐证了费马小定理的逆命题不成立。

  不过 卡迈克尔数 的出现密度较低,在一亿以内的自然数中也仅仅只有 255255255 个。


二次探测定理

Strong probable primes


  若 ppp 为质数,则 x2≡1(modp)x^2 \equiv 1 \pmod px2≡1(modp) 的解为 x≡±1(modp)x \equiv\pm 1 \pmod px≡±1(modp)。

  但这个特性并非质数的精确表征,因此通过该式测试的数 ppp,被称为 强可能质数,而通过该式测试的合数则被称为 强伪质数。


非平凡平方根


  截止至最后一次更新,搜索引擎上很多有关 米勒-拉宾 测试都未对该点讲解,所以这里简单介绍一下。

  若 x≢±a(modp)x \not\equiv\pm\sqrt a\pmod px​≡±a​(modp),满足 x2≡a(modp)x^2 \equiv a\pmod px2≡a(modp),则称 xxx 是以 ppp 为模的 aaa 的 非 平凡\显然 平方根。

  特别地,当 ppp 为质数时,aaa 不存在非平凡平方根。


Miller-Rabin 素性测试


  米勒-拉宾 素性测试就是将:费马小定理、二次探测定理、非显然平方根的性质结合起来。

  具体地说:

  给定一个整数 nnn,我们先判断它是否为偶数,若是则判断是它是否为 aaa,不是则存在一个 bbb,n=b×2k+1n = b ×2^k + 1n=b×2k+1,kkk 尽可能的大,此时随机一个 aaa,根据费马小定理,若 nnn 为质数,则存在一个 k′<kk' < kk′<k,使得 ab⋅2k′≡±1(modn)a^{b\cdot2^{k'}} \equiv \pm 1\pmod nab⋅2k′≡±1(modn) 成立,否则 aaa 存在一个模 nnn 下的非显然平方根,此时 nnn 必为合数。

  一次 米勒-拉宾 素性测试的误判在 14\frac 1441​,因此建议对于每个数的测试次数都在 888 次以上。

  关于上述概率的证明,等我实在是太闲的时候补。


bool is_prime(int n) {if ((n & 1) == 0) return n == 2;int b = n - 1, k = 0, j;while (b & 1 == 0) b >>= 1, ++k;for (int i = 0; i < test_time; ++i) {int a = rand() % (n - 1) + 1;int v = mod_pow(a, b, n);if (v == 1) continue;for (j = 0; j < k; ++j) {if (v == n - 1) break;v = v * v % n;}if (j == b) return 0;}return 1;
}

查找因数


  在程序设计竞赛中,算术基本定理表明,我们可以将任意一个大于 111 自然数表示为若干个质数的乘积,即N=p1a1⋅p2a2⋅p3a3⋅⋯⋅pss1=∏i=1spiaiN=p^{a_1}_1\cdot p^{a_2}_2\cdot p^{a_3}_3\cdot \cdots \cdot p^{s_1}_s =\prod_{i=1}^sp^{a_i}_iN=p1a1​​⋅p2a2​​⋅p3a3​​⋅⋯⋅pss1​​=i=1∏s​piai​​

  这是很多带有数论标签的题目的突破口,

  因此,掌握如何快速分解一个整数在程序设计竞赛中尤为的重要。


还是试除法


  既然试除法可以通过 1∼N1 \sim \sqrt N1∼N​ 之间是否存在 NNN 的因数来判断 NNN 是否为质数,那么同样的,

  在试除法的基础上稍作修改,就能计算出 NNN 的所有质因子。

std::map<int, int> factor(int N) {std::map<int, int> factors;for (int p = 2; p <= sqrt(N); ++p)if (N % p == 0) {int k = 0;while (N % p == 0) N /= p, ++k;factors[p] += k;}if (N != 1) ++factors[N];return factors;
}

  朴素的试除法复杂度在 O(N)O(\sqrt N)O(N​),若在有质数表的情况下试除则复杂度降至 O(2Nlog⁡N)O(\cfrac{2\sqrt N}{\log N})O(logN2N​​)。

  关于 质数打表 点这里。


生日悖论


  Pollard’s Rho 分解质因数的期望效率是反直觉的,因此在介绍 波拉德的 ρ\rhoρ 之前需要先引入生日悖论。

  原命题我是在《费马大定理》上看到的,但现在书不在手边,就在网上随便搜了一个:

  在一场英式足球赛里,通常会有 23 个人在赛场上:两支参赛队伍各有 11 名球员,外加 1 名裁判。在这23 人里,2 人或 2 人以上具有相同生日的概率是多少?

  答案是约为 505050%,这对于大多数没有接触过概率论的人来说都是反直觉的。

  针对没有接触过概率的读者,这里简单验证一下:

  设 AAA 为事件 nnn 个人生日不同,P(A)P(A)P(A) 为事件 AAA 发生的概率,则 AAA 的逆命题 Aˉ\bar AAˉ,nnn 个人中至少有两个人生日相同的概率为 P(Aˉ)=1−P(A)P(\bar A) = 1 - P(A)P(Aˉ)=1−P(A)。P(A)=365365×365−1365×365−2365×⋯×365−n+1365=∏i=0n−1365−i365P(A) = \frac{365}{365} × \frac{365 - 1}{365} × \frac{365 - 2}{365} × \cdots × \frac{365 - n + 1}{365} = \prod_{i=0}^{n-1}\frac{365-i}{365}P(A)=365365​×365365−1​×365365−2​×⋯×365365−n+1​=i=0∏n−1​365365−i​  该式的意义为,

  111 个人时生日不同的概率显然为 100100100%,而第 222 个人的开始,为了与前面的人生日不同,只能从前面的人生日的其他日期任选一个,于是第 222 个人有 364365\frac{364}{365}365364​ 的概率与前面所有人的生日不同,第 333 人为 363365\frac{363}{365}365363​,⋯\cdots⋯,最终这个事件的总概率为它们的乘积。

  借助计算机,我们可以得知,当 n=23n = 23n=23 时,P(Aˉ)=1−P(A)≃0.5P(\bar A) = 1 - P(A) \simeq 0.5P(Aˉ)=1−P(A)≃0.5,当 n=57n = 57n=57,P(Aˉ)≃0.99P(\bar A) \simeq 0.99P(Aˉ)≃0.99。


伪随机数列


  生日悖论启示我们,某个长度为 nnn,分布在为 [1,N][1,N][1,N] 的随机数列 x1,x2,x3,⋯,xnx_1, x_2, x_3, \cdots , x_nx1​,x2​,x3​,⋯,xn​,它们中最少出现两个相等数字的期望长度不会很大(实际为 πN2\sqrt{\cfrac{\pi N}2}2πN​​)。

  设 NNN 的某个因子为 ppp,若我们不断的在 [1,N)[1,N)[1,N) 之间生成随机数 xix_ixi​,同时构造一个随机数列 {yi∣yi=ximodp}\{y_i \mid y_i = x_i \mod p\}{yi​∣yi​=xi​modp},那么当存在一对 iii、jjj,使得 xi≠xjx_i \neq x_jxi​​=xj​,yi=yjy_i = y_jyi​=yj​ 同时成立,另 d=yi−yjd = y_i - y_jd=yi​−yj​,显然有 d≡0(modp)d \equiv 0\pmod pd≡0(modp),1<gcd⁡(d,N)<N1 < \gcd(d,N) <N1<gcd(d,N)<N,此时 ∣d∣\mid d\mid∣d∣ 为 NNN 的一个因子。

  考虑最坏情况,即 N=p2N = p^2N=p2,ppp 为一个质数(NNN 本身就是质数的情况可以先用 Miller-Rabin 素性测试特判一下),此时 p=Np = \sqrt Np=N​,{yi}\{y_i\}{yi​} 的期望长度为 πN2≃N14\sqrt{\cfrac{\pi \sqrt N}2} \simeq N^{\frac 14}2πN​​​≃N41​。


Pollard’s Rho


  光整合上述知识,想要快速的将一个较大整数 NNN 分解为算术基本定理形式也是不够的,光枚举 ddd 的复杂度就已经到了 O(N)O(\sqrt N)O(N​),更别提对于每一次枚举还要做一次 gcd⁡\gcdgcd。

  所以 Pollard 选择一种特殊的伪随机数生成器,xi=xi−12+c(modN)x_i = x_{i-1}^2 + c \pmod Nxi​=xi−12​+c(modN),其中 ccc 为某个固定常数,x1x_1x1​ 随机取得。

  例如当 N=41,x1=31,c=5N = 41,x_1 = 31,c=5N=41,x1​=31,c=5 时,生成的随机数列为:31,23,1,6,0,5,30˙,3,14,37,21,36˙,30,⋯31,23, 1, 6, 0, 5, \dot{30}, 3, 14, 37, 21, \dot{36}, 30,\cdots31,23,1,6,0,5,30˙,3,14,37,21,36˙,30,⋯  将数列按下图所示方式排列,会发现性质酷似一个 ρ\rhoρ,算法也因此得名。

  (图片摘自 wiki)

  主流的优化方式有 Floyd 判环法,倍增法,这里仅对 判环法 做出阐述和实现。

  若 {xi}\{x_i\}{xi​} 上存在一对 iii、jjj 使得 xi−xj≡0(modp)x_i - x_j \equiv 0\pmod pxi​−xj​≡0(modp),则有

  xi−xj≡xi−12−xj−12≡(xi−1+xj−1)(xi−1−xj−1)≡0(modp)x_i - x_j \equiv x_{i-1}^2 - x_{j-1}^2\equiv (x_{i-1} + x_{j-1})(x_{i-1} - x_{j-1})\equiv 0\pmod pxi​−xj​≡xi−12​−xj−12​≡(xi−1​+xj−1​)(xi−1​−xj−1​)≡0(modp),

  该式表明了,对于所有 k,gk,gk,g,k−g=i−jk-g=i-jk−g=i−j,都有 xk−xg≡0(modp)x_k - x_g\equiv 0\pmod pxk​−xg​≡0(modp),因此使用 Floyd 判环法,依次枚举环上距离为 1∼n1 \sim n1∼n 的随机数对,期望枚举次数为 n=N14n=N^\frac 14n=N41​,

  整个算法的期望复杂度为 O(N14log⁡N)O(N^\frac 14\log N)O(N41​logN)。

int f(int x, int c) { return x * x + c; }int gcd(int a, int b) { return b ? gcd(b, a % b) : a; }int pollard_rho(int N, int c) {int xi = rand() % (N - 1) + 1, xj = f(xi, c) % N;while (xi != xj) {int d = gcd(N, xi - xj);if (d > 1) return d;xj = f(f(xj, c), c) % N;xi = f(xi, c) % N;}return N;
}

分解质因数


  综合上述所有知识点,我们可以确定出一个算法,可以在较为优秀的复杂度内完成对一个大整数的分解。

  具体地说,对于一个整数 NNN,我们先对其进行素性测试,若是则将其加入质因数集合,否则使用 Pollard’s Rho 找到他的一个非平凡因子 ddd,将 ddd 和 N/dN / dN/d 代回到第一步。

#include <stdio.h>
#include <math.h>
#include <map>long long multi(long long a, long long b, long long p) {long long res = 0;while (b) {if (b & 1) res = (res + a) % p;a = (a + a) % p;b >>= 1;}return res;
}long long qpow(long long a, long long b, long long p) {long long res = 1;while (b) {if (b & 1) res = multi(res, a, p);a = multi(a, a, p);b >>= 1;}return res;
}long long gcd(long long a, long long b) { return b ? gcd(b, a % b) : a; }int test_time = 8;bool miller_rabin(long long n) {if (n < 3 || n % 2 == 0) return n == 2;long long b = n - 1, k = 0, j;while (b % 2 == 1) b /= 2, ++k;for (int i = 0; i < test_time; ++i) {long long a = qpow(rand() % (n - 2) + 2, b, n);if (a == 1) continue;for (j = 0; j < k; ++j) {if (a != n - 1) break;a = multi(a, a, n);}if (j == k) return 0;}return 1;
}long long f(long long x, long long c, long long p) { return (multi(x, x, p) + c) % p; }long long pollard_rho(long long N, long long c) {long long xi = rand() % (N - 1) + 1, xj = f(xi, c, N);while (xi != xj) {long long d = gcd(xi - xj, N);if (d > 1) return d;xj = f(f(xj, c, N), c, N);xi = f(xi, c, N);}return N;
}void factor(long long N, std::map<long long, int> &factors) {if (miller_rabin(N)) ++factors[N];else {long long c = rand() % (N - 1) + 1;long long d = N;while (d >= N)d = pollard_rho(N, c--);factor(N / d, factors);factor(d, factors);}
}int main(){long long N;while (~scanf("%lld", &N)) {std::map<long long, int> factors;printf("%lld=", N);factor(N, factors);for (std::map<long long, int>::iterator it = factors.begin(); it != factors.end();) {printf("%d^%d", it->first, it->second);if (++it != factors.end()) printf("*");}printf("\n");}
}

分解质因数-Pollard‘s Rho相关推荐

  1. C++实现的大整数分解Pollard's rho算法程序

    代码来自GeeksforGeeks的Pollard's Rho Algorithm for Prime Factorization. C++语言程序代码如下: /* C++ program to fi ...

  2. python大数快速判断质数与分解质因数

    python 大数质因数分解 数字较小时: def is_prime(number):for i in xrange(2, int(math.sqrt(number))+2):if number %i ...

  3. Magic的Miller Rabin素数测定和Pollard's Rho质因子分解法

    boshi大佬帮助了litble,使愚蠢的litble(可能?)学会了这种神奇的算法. orz boshi. 例题:poj1811 Miller Rabin 又称米勒挝饼, 一种神奇的快速判定一个数是 ...

  4. 关于质因数,分解质因数

    质因数(素因数或质因子):在数论里是指能整除给定正整数的质数. 互质:除了1以外,两个没有其他共同质因子的正整数称为互质. 因为1没有质因子,1与任何正整数(包括1本身)都是互质. 正整数的因数分解可 ...

  5. Java实现算法导论中Pollard的rho启发式方法

    Pollard的rho启发式方法用于启发式求解大整数n分解因子,具体要结合导论中来理解,参考代码如下: package cn.ansj;import java.math.BigInteger; imp ...

  6. 质因子分解算法c语言prime,分解质因数的算法

    满意答案 fy7306 2013.04.20 采纳率:49%    等级:12 已帮助:11721人 1.素数表,从小到大去试除,一直到当前质数的平方大于试除后剩下的数. 这样优化后的效率会比较高,至 ...

  7. 【快速因数分解】Pollard's Rho 算法

    算法目的 给一个数n,快速提取n的一个因数. 算法根据:生日悖论 讲生日悖论之前,先看一个东西. 给出[1-1000]的数,从中任意选出一个数为k的概率是110001\over 100010001​. ...

  8. [玄学]——数论高级之分解质因数(Pollard_rho)(POJ 1811)

    前言 这是一个伤心的故事:我已经好久没有写博客了... 当然这次跟大家分享的是一个很玄学的东西--Pollard_rho 为什么说他很玄学呢?大家可以在等下的代码中看出来(全是rand...) 当然, ...

  9. 两个质数互质是_两个质数一定是互质数_互质数和质数的区别_分解质因数的方法_互为质数和互质数...

    宜城教育资源网www.ychedu.com两个质数一定是互质数_互质数和质数的区别_分解质因数的方法_互为质数和互质数质数,互质数,分解质因数,合数一个数只有1和它本身两个约数,这样的数叫做质数.一个 ...

最新文章

  1. 【Dlib】dlib实现深度网络学习之 input层
  2. c/c++文件I/O函数学习--不断补充
  3. 单向板的受力示意图_成品单向滑动铰支座的安装使用
  4. PHP 实现移除数组中项目为空的元素或为某值的元素
  5. 分享25个新鲜出炉的 Photoshop 高级教程
  6. 【OS学习笔记】一 处理器、内存和指令
  7. tensorflow代码中tf.app.run()什么意思
  8. linux修改last权限,Linux常用命令2/3(有关用户、权限管理的命令)--Unix/Linux操作系统04...
  9. mysql的字段长度_【mysql】字段类型和长度的解释
  10. 支持Flash和JavaScript的图表控件FusionWidgets
  11. sqlserver日期函数 dateadd,datediff ,datepart ,datename,convert
  12. C#面向对象编程的3个支柱
  13. Dedecms之SQL语句修改和调用数据总结
  14. js 数组 常用方法
  15. 麦咖啡McAfee 8.8企业版规则设置(高级篇)
  16. 【无标题】AMAZINGIC晶焱科技:预防TVS闩锁风险的方法与实际案例分析
  17. Wonderware-InTouch 报表查询SQL数据库,用表格控件做出的报表图例
  18. IDEA 黄色警告 found duplicated code in this file finds duplicated code
  19. sqlserver 导入导出数据向导
  20. CORBA 架构体系指南(通用对象请求代理体系架构)​

热门文章

  1. 简历制作案例分析及制作小技巧总结
  2. FlashFXP.v3.5.4.1230-RES-patch by SunBeam
  3. android线程改变布局,震惊!Android子线程也能修改UI?(第二篇)
  4. word多个标题一不能显示在一页
  5. 星环科技:坚持国产自主路线,突破大数据的细分应用场景
  6. python模块之StringIO
  7. 校招----shein一面面经
  8. 芝诺数解|【二】风禾尽起,且住为佳——重庆租房
  9. 【JavaScript 逆向】极验三代无感验证码逆向分析
  10. GB/T28181-2022协议版本标识X-GB-Ver解读