BISHI39 【模板】Pollard-Pho算法¶
中等通过率 34.15%python3样例通过牛客 AC
讲解章节:整除分块与数论进阶
一句话
【模板】Pollard-Rho 算法 —— 判定 x (<= 1e12) 是否为质数。
解题思路¶
这题考什么¶
题目挂着 Pollard-Rho 的名字,但真正问的只是素性判定, 并不需要把 x 分解出来,所以核心是 Miller-Rabin。 (Pollard-Rho 是「找非平凡因子」的算法,它内部还要靠 Miller-Rabin 判断递归边界;这里既然只要 Yes/No,就没必要真去分解。)
Miller-Rabin:把 n-1 写成 d * 2^s(d 为奇数),对底数 a 检查
否则 n 必为合数。Python 的内置 pow(a, d, n) 就是 C 实现的快速幂, 自带模乘,不用手写龟速乘(C++ 里 1e12 的模乘会爆 long long, 要用 __int128 或 Montgomery,Python 完全没这个烦恼)。
确定性底数:已知对 n < 3,474,749,660,383(约 3.47e12)而言, 底数集合 {2,3,5,7,11,13} 的 Miller-Rabin 是确定性的、不会误判。 本题 x <= 1e12 < 3.47e12,所以直接用这 6 个底数即可。 这样一来算法里完全没有随机数,同一输入多次运行结果必然一致, 也不存在「随机种子导致偶发 WA」的问题。
数据规模与复杂度¶
T <= 1e5,x <= 1e12。 单次 Miller-Rabin 是 O(k log x) 次模乘(k = 6,log x ≈ 40)。 优化两处以压常数:
- 先用小素数(< 100)试除,绝大多数合数在这里就被筛掉, 只有少数「大素数 / 大半素数」才走到 Miller-Rabin;
- 用字典缓存已算过的 x(T 到 1e5 时重复询问很常见)。
IO 用 buffer.read().split(),输出攒 list 一次写出。
坑在哪¶
- x = 1 不是质数,必须特判(n-1 = 0 会让 Miller-Rabin 逻辑失效);
- 底数 a 可能等于 n 本身(例如 n = 2,3,5,7,11,13), 此时 a % n == 0,要先把这些小素数直接判 Yes 再进主流程;
- 输出是 Yes / No(首字母大写,其余小写),不是 YES/NO;
- Python 3.9 兼容:pow 的三参数形式(模幂)从 2.x 就有,不依赖新版本特性;
- 内层循环写成 for ... else:只有当循环没有被 break 打断时才执行 else, 正好对应「s-1 次平方里始终没出现 n-1」这一判合数的条件。 误把 else 缩进到 for 内部或写成 if,判定就会反过来。
快速幂与模运算见 81-快速幂与逆元, 素数与因数的基础见 80-数论基础。
参考实现¶
[:octicons-arrow-left-16: BISHI38](BISHI38.md) [BISHI40 :octicons-arrow-right-16:](BISHI40.md)