Pollard-Rho 算法可以用期望 $O(\sqrt[4]{n})$ 的时间找到合数 $n$ 的一个非平凡因子。
算法流程
这里讲解改进后的 Pollard-Rho 算法变种。
在一次 Pollard-Rho 中,我们随机一个数 $c$,并设定阈值 $k$,初始为 $2$,每次循环后加倍。
对于每一次循环,令 $y$ 等于当前的 $x$,然后进行 $k$ 次,每次让 $x \leftarrow f(x) = (x ^ 2 + c) \bmod n$,然后检查 $\vert x - y \vert$ 和 $n$ 是否有非平凡的 $\gcd$,如果有则退出循环。
实践后发现这样速度较快,这可说明该算法的复杂度较低,但是笔者不会严格证明算法的复杂度。有一个小优化,就是每次不直接检查 $\gcd$,而是将变量 $z \leftarrow z \times \vert x - y \vert$。进行 $B$ 次乘法后只要检查一次 $z$ 和 $n$ 的 $\gcd$ 即可。这可以节省 $\gcd$ 使用的时间,$B$ 一般使用 $128$。
Pollard-Rho 结合 Miller-Rabin 可以实现快速素因数分解。
代码实现
「模板」Pollard-Rho 算法(Luogu 4718)12345typedef long long ll;6int T;7ll n, ans;89inline ll mult(const ll &a, const ll &b, const ll &m) {10 return (__int128) a * b % m;11}1213inline ll func(const ll &x, const ll &n) {14 return x < n ? x : x - n;15}1617ll qpow(ll a, ll b, ll m) {18 ll c = 1;19 for (; b; b >>= 1, a = mult(a, a, m)) {20 if (b & 1) c = mult(a, c, m);21 }22 return c;23}2425bool miller(ll m, ll d, ll r, ll n) {26 if (m > n - 2) return true;27 ll x = qpow(m, d, n);28 if (x == 1 || x == n - 1) return true;29 for (int i = 0; i < r; i++) {30 x = mult(x, x, n);31 if (x == n - 1) return true;32 }33 return false;34}3536bool is_prime(ll n) {37 if (n <= 2) return n == 2;38 static ll m[] = { 2, 325, 9375, 28178, 450775, 9780504, 1795265022 };39 ll d = n - 1, r = 0;40 while (~d & 1) d /= 2, r++;41 for (int i = 0; i < 7; i++) {42 if (!miller(m[i], d, r, n)) return false;43 }44 return true;45}4647ll randl() {48 return rand() | ((ll) rand() << 31);49}5051ll gcd(ll a, ll b) {52 return b ? gcd(b, a % b) : a;53}5455ll absl(ll x) {56 return x < 0 ? -x : x;57}5859ll factor(ll n) {60 ll c = randl() % (n - 1) + 1, x = 0, y = 0, d = 1;61 for (ll k = 2, t = 1; ; k *= 2, y = x, t = 1) {62 for (ll i = 1; i <= k; i++) {63 x = func(mult(x, x, n) + c, n), t = mult(t, absl(x - y), n);64 if (!(i & 127) && (d = gcd(t, n)) > 1) break;65 }66 if (d > 1 || (d = gcd(t, n)) > 1) break;67 }68 return d;69}7071void solve(ll n) {72 if (n == 1 || n <= ans) return;73 if (is_prime(n)) {74 ans = n;75 return;76 }77 ll x = factor(n);78 while (x == n) x = factor(n);79 while (n % x == 0) n /= x;80 solve(x), solve(n);81}8283int main() {84 scanf("%d", &T);85 srand(time(0) ^ T);86 while (T--) {87 scanf("%lld", &n);88 ans = 0;89 solve(n);90 if (ans == n) {91 puts("Prime");92 } else {93 printf("%lld\n", ans);94 }95 }96 return 0;97}