P4718
【模板】Pollard-Rho算法
实际上是 Pollard-Rho 和 Miller-Rabin 的缝合模板题。
首先关于 Miller-Rabin 素性测试。
很多关于这方面的数学上的解释我现在做不来,所以只能记录一下使用。。。
费马素性测试:如果对于 $a \in ( 1 , p ) $,存在 $a ^ { p - 1 } \not \equiv 1 \pmod { p } $,则 $p$ 一定不是一个素数;反之,若有若干个 $a$ 满足 $a ^ { p - 1 } \equiv 1 \pmod{p}$,则 $p$ 大概率是素数。
因此我们随机几个 $a$ 来测试素性。
这是个很玄学的方法,正确性的保证不高。
有的合数也能满足 $\forall a \in (1,p) , a ^ { p - 1 } \equiv 1 \pmod {p}$,被称为卡迈尔数,或者费马伪素数。
$561 = 3 \times 11 \times 17$ 就是一个卡迈尔数,并且若 $n$ 是一个卡迈尔数,则 $m = 2 ^ n - 1$ 也是一个卡迈尔数,即其个数是无穷的。最重要的是在 $10 ^ 9$ 内,这样的合数有 $646$ 个,想拿这个混作正经素性测试还是省省吧。
于是我们需要考虑优化这个素性测试。
二次探测定理:若 $p$ 为奇素数,则 $x ^ 2 \equiv 1 \pmod { p }$ 的解为 $x \equiv \pm 1 \pmod{ p }$。
我们以原本的费马素性测试作为依托,如果 $a ^ { p - 1 } \equiv 1 \pmod{p}$ 不成立的话,直接判为合数即可;反之我们尝试使用二次探测定理继续判断。
至于如何使用二次探测定理继续判断,我们先将 $p-1$ 分解为 $p - 1 = d \times 2 ^ {r}$,其中 $d$ 是一个奇数。
首先如果 $a ^ d \equiv \pm 1 \pmod { p }$,这是满足二次探测定理的,我们认为其通过了这次子测试。
反之我们尝试将 $d$ 不断乘 $2$(实际操作中将 $a ^ d$ 进行平方),最多乘 $r - 1$ 次,如果说存在 $a ^ d \equiv - 1 \pmod { p }$,我们也认为其通过了这次子测试(不可能等于 $1$ 的,不然最开始的 $a ^ d$ 一定是 $1$)。
反之说明其没有通过本次子测试,一定是个合数。
我们设 $x = a ^ { ( p - 1) / 2 } $,实际上这个过程就是说明 $x ^ 2 \equiv 1 \pmod { p }$ 的解是 $x \equiv \pm 1 \pmod { p }$,满足二次探测定理,而如果没有通过子测试,说明 $x \not \equiv \pm 1 \pmod { p }$,这是违背二次探测定理的,说明 $p$ 一定不是奇素数。
这种通过二次探测定理优化的素性测试即 Miller-Rabin 素性测试。
实际操作中,我们随机不少于 8 次 $a$,比如 10 次。这样既保证正确率比较高,也不会过于影响效率。
当然,在 OI 的环境下,数字一般是 $2 ^ { 6 4 }$ 以内的,我们实际上可以通过选取固定的 $a$ 来实现快速的确定性判素。
- 对于 $2 ^ { 3 2 }$ 以内判素,选取 $2,7,61$ 三个数即可。
- 对于 $2 ^ { 6 4 }$ 以内判素,选取 $2,325,9375,28178,450775,9780504,1795265022$ 七个数即可。
- 在考场上,出于这些数字的背诵没什么必要,我们用前十二个质数(到 $37$)即可实现 $2 ^ { 7 8 }$ 确定性判素。
至于原理,我完全不知道,但是感性理解应该并不难吧?
测试一次的复杂度是 $O( k \log ^ 3 p)$ 的。其中 $k$ 是测试轮数。
Pollard-Rho 是一个更玄学的东西,可以在期望 $O( n ^ {1 / 4})$ 的复杂度下求出 $n$ 的一个非平凡因子。所谓非平凡因子,就是 $( 1 , n )$ 内的一个 $n$ 的因子。
我们先构造伪随机数:令 $f(x)= (x ^ 2 + c ) \bmod n$,其中 $c$ 是一个随机常数。
然后初始随机取一个 $x _ 1$,其后每次 $x _ i = f ( x _ { i - 1 })$。
这个伪随机数是有循环节的,类似下面的图。
长得很像 $\rho$,所以叫做 Pollard-Rho(所以按理说这个 Rho 应该小写吗?)。
根据生日悖论,这个环和尾的期望长度都是 $O( \sqrt {n})$ 的。
在这一堆随机数中,我们尝试选取 $x _ i $ 和 $x _ j $ 构造 $| x _ i - x _ j |$,通过 $\gcd ( | x _ i - x _ j | , n)$ 来找到 $n$ 的一个非平凡因子。由于某些神奇的原因,在这种优秀的随机数列下,我们能在期望下 $O( n ^ {1 / 4})$ 的复杂度下找到这样的因子。
有一种 Floyd 判环的方法,其方法就是使 $a = f ( a )$ 单次迭代,而 $ b = f ( f ( b ))$ 二重迭代,造成二者的差距,从而构造出 $x _ i $ 和 $x _ j$。其间若发现成环(就是 $a$ 和 $b$ 相等了),直接退出即可。反之若发现平凡因子,直接返回即可。
但是这种实现下,$\gcd$ 的调用次数很多,因此我们通过倍增优化来减少调用 $\gcd$ 的次数。
具体来说,我们每次固定下 $x _ i$,然后倍增式多次迭代 $x _ j$(第一次迭代 1 次,第二次 2 次,第三次 4 次,以此类推),并令 $s = \prod | x _ i - x _ j | $,一旦 $s = 0$ 说明分解失败,返回 $n$(下一次再用 Pollard-Rho 尝试分解,直到分解出来,因为经过 Miller-Rabin 之后我们判断这个数肯定不是素数)。
过程中,每隔 $2 ^ k - 1$ 个数就查询一次 $\gcd$。这里取 $k=7$。
对于本题找最大质因子的需求,不断分解并判素即可。
代码:
1 |
|