Pollard-Rho 分解算法学习笔记
Pollard-Rho 分解算法
Pollard-Rho 算法是一种用于快速找到
生日悖论
在不少于
更一般的形式,随机选取在
用下面的方法可以简单估计一下。
Miller-Robin 素性测试
由于Pollard-Rho是用来寻找非平凡因子的,我们需要提前判断待分解数是否为素数。
费马小定理
若正整数
- proof:
显然 是 的一个完全剩余系。
也是 的一个完全剩余系。
否则假设 那么 而 所以 出现矛盾。
因此有
但遗憾的是,费马小定理的逆命题并不成立,例如
我们只能用费马小定理来判断一个数
我们不总能通过增加参与测试的
二次探测定理
若
- proof:
米勒检验
我们可以在费马素性测试的基础上利用二次探测定理进行进一步测试,如果
令
数学家们已经证明,若
可以验证,在
Pollard-Rho分解
根据生日悖论,我们随机的选取$\left[ 1,N \right]
经验表明,在Pollard-Rho算法中选取,
下面的图可以直观的感受到Rho是什么意思
这说明如果
那么如何找到这个环呢,记录出现过的数,并在里面查找是不够优秀的,因为还要算上查找的时间复杂度。我们可以引入两个变量
但是每次都求
另外Pollard-Rho往往在分解拥有大质因数的正整数时比较有效,在实际应用中可以先用试除法分解掉较小的质因子再用Pollard-Rho分解。
code:【模板】Pollard rho 算法
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <iostream>
#define int long long
using namespace std;
const int d[12] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
int prime[200005], a[1000005], n, cnt = 0, div1[1005][2];
int gcd(int o, int p) { return (!p) ? o : gcd(p, o % p); }
int mi(__int128 o, int p, int mo) {
__int128 yu = 1;
while (p) {
if (p & 1) yu = yu * o % mo;
o = o * o % mo;
p >>= 1;
}
return yu;
}
bool test(int o, int p) {
int yu = p - 1;
if (mi(o, yu, p) != 1) return 0;
while (!(yu & 1)) {
yu >>= 1;
if (mi(o, yu, p) == (p - 1)) return 1;
}
return mi(o, yu, p) == 1;
}
bool Miller_Rabin(int o) {
if (o <= 2) return 1;
for (int i = 1; i < 12; i++)
if (o == d[i]) return 1;
for (int i = 0; i < 12; i++)
if (!test(d[i], o)) return 0;
return 1;
}
int Pollard_Rho(int n) {
int x1 = 1ll * rand() * rand() * rand() * rand() % n + 1, y = x1,
c = rand() + 1;
for (int lim = 1;; lim = min(lim << 1, 128ll)) {
int cnt = 1;
for (int i = 1; i <= lim; i++) {
x1 = (__int128)x1 * x1 % n + c;
y = (__int128)y * y % n + c;
y = (__int128)y * y % n + c;
cnt = (__int128)cnt * (x1 - y) % n;
}
int yu = gcd(cnt, n);
if (yu > 1) return yu;
}
return n;
}
signed main() {
srand((unsigned)time(NULL));
for (int i = 1; i <= 100000; i++) a[i] = 0;
for (int i = 2; i <= 100000; i++) {
if (!a[i]) prime[++cnt] = i;
for (int j = 1; (j <= cnt) && (prime[j] * i <= 100000); j++) {
a[prime[j] * i] = 1;
if (i % prime[j] == 0) break;
}
}
int T;
cin >> T;
while (T--) {
int n, s0 = 0;
scanf("%lld", &n);
if (Miller_Rabin(n)) {
printf("Prime\n");
continue;
}
for (int i = 1; i <= cnt; i++) {
if (n % prime[i] == 0) {
s0++;
div1[s0][0] = prime[i];
div1[s0][1] = 0;
while (n % prime[i] == 0) {
n /= prime[i];
div1[s0][1]++;
}
}
}
while (!Miller_Rabin(n)) {
int yu = Pollard_Rho(n);
n /= yu;
if (yu > n) swap(n, yu);
if (yu > 1) {
div1[++s0][0] = yu;
div1[s0][1] = 1;
while (n % yu == 0) div1[s0][1]++, n /= yu;
}
}
if (n > 1) div1[++s0][0] = n, div1[s0][1] = 1;
int ans = 0;
for (int i = 1; i <= s0; i++) ans = max(ans, div1[i][0]);
printf("%lld\n", ans);
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 一文读懂知识蒸馏
· 终于写完轮子一部分:tcp代理 了,记录一下