pollard-rho
补写算法流程。
生日悖论:值域为 \(n\),时,期望随机 \(O(\sqrt{n})\)(OI-wiki 上给的是 \(\sqrt{2 n \ln 2}\))个数有数字相同。(感觉有点奇怪,原表述是这么多次有数字相同的概率是 \(\frac{1}{2}\)。)
算法流程:
- 尝试分解 \(n\) 的最小非平凡因子 \(m\)
- 设 \(f(x) = x^2 + c\),所有运算模 \(n\)。\(x \equiv y, f(x) \equiv f(y)\)。
- 显然 \(f\) 最后会进入环,生成的东西就像 \(\rho\)。
- 把 \(f(x)\) 看作随机数,则环的长度是 \(O(\sqrt{n})\) 级别。
- \(m\) 若不是 \(n\),则 \(m\) 不大于 \(\sqrt{n}\)。把 \(f\) 中每一项对 \(m\) 取模,期望 \(O(n^{\frac{1}{4}})\) 次有重。故期望 \(O(n^{\frac{1}{4}})\) 种不同数字。
- 随机选取 \(x_i, x_j\),有 \(\dfrac{1}{4}\) 概率 \(m \mid x_i-x_j\),此时提取 \(\gcd(n, |x_i-x_j|)\),是 \(m\) 的倍数。
- 考虑判环,这里写倍增判环。每隔 \(2^i\) 步,找一个在序列中的基准点 \(t\),判 \(\prod (t - x_i)\) 是否为 \(0\)。
- \(\gcd\) 挺耗时间,隔一定间隔把要求 \(\gcd\) 的数字乘起来一起求也行。
挺对!
补写 MR 素性测试。
- \(a^{n-1} \equiv 1 \pmod n\) 在 \(n=p\) 时是对的,但是不能推出 \(n=p\)。
- \(a^2 \bmod p\) 两两成对,二次探测定理:\(x^2 \equiv 1 \pmod p\) 只有平凡解。
- 考虑 \(a^{n-1} \equiv 1 \pmod n\) 且 \(n\) 不是素数,把 \(n\) 分解为 \(2^u \cdot v\),逐一检测 \(a^{v \cdot 2^i}\),若出现 \(1\),则非平凡解出现,则 \(n\) 不是素数。
- 我不大理解为什么这样留下来的很大概率是素数,但是它应当就是对的。
P4718:
#include <bits/stdc++.h>
#define LL long long
__int128 l = 1;
LL qpow(LL a, LL b, LL p) {
LL ans = 1;
for (; b; b >>= 1) {
if (b & 1) ans = l * ans * a % p;
a = l * a * a % p;
}
return ans;
}
std::default_random_engine e(time(0));
const int limmr = 8;
bool mr(LL n) {
if (n % 2 == 0 && n > 2) return 0;
if (n <= 3) return n != 1;
LL u = n - 1, t = 0;
while (u % 2 == 0) u /= 2, ++t;
for (int i = 0; i < limmr; i++) {
LL a = e() % (n - 2) + 2, v = qpow(a, u, n);
if (v == 1 || v == n - 1) continue;
LL s = 0;
for (; s < t; ++s) {
if (v == n - 1) break;
v = l * v * v % n;
}
if (s == t) return 0;
}
return 1;
}
const int step = 127;
LL pr(LL n) {
LL c = e() % (n - 1) + 1;
LL t = 0, s = t;
LL val = 1;
for (LL lim = 1; ; lim <<= 1, t = s, val = 1) {
for (LL st = 0; st < lim; ++st) {
s = (l * s * s + c) % n;
val = l * val * abs(t - s) % n;
if (st % step == 0) {
LL d = std::__gcd(val, n);
if (d > 1) return d; // 期望 m^{1/4} 随到 p 倍数
}
}
LL d = std::__gcd(val, n);
if (d > 1) return d;
}
}
LL maxfactor;
void fac(LL n) {
if (n <= maxfactor || n == 1) return ;
if (mr(n))
return maxfactor = std::max(maxfactor, n), void();
LL p = n;
while (p >= n) p = pr(n);
while (n % p == 0) n /= p;
fac(p), fac(n);
}
int main() {
int T; scanf("%d", &T); while (T--) {
LL n; scanf("%lld", &n);
maxfactor = 0;
if (!mr(n)) fac(n), printf("%lld\n", maxfactor);
else printf("Prime\n");
}
return 0;
}
/*
stupid mistakes:
- t=s 而非 s=t
- __int128 那里打括号
*/
本文来自博客园,作者:purplevine,转载请注明原文链接:https://www.cnblogs.com/purplevine/p/pollard-rho.html