boolis_prime(uint64_t n){ for (uint64_t p = 2; p * p <= n; ++p) if (n % p == 0) returnfalse; returntrue; }
优化 1
在对 试除后,我们显然可以不再使用偶数试除。
1 2 3 4 5 6 7 8
boolis_prime(uint64_t n){ if (n % 2 == 0) return n == 2; for (uint64_t p = 3; p * p <= n; p += 2) if (n % p == 0) returnfalse; returntrue; }
优化 2
由于大于 的质数只可能是 的形式,因此可以进一步优化。
这个常数上优化应该不大,不给出实现了。
枚举质数
这个在复杂度上是有优化的,因此与暴力枚举分开。
可以将 以内的质数全部使用筛法预处理掉,根据前文结论,这个量级大概是 的。
预处理质数后分解质因数的效率未必劣于 Pollard Rho 算法。
这种方法的一个神秘优化是在预处理质数的同时预处理其快速取模算法,不知道常数上的优化效果。
上述两种方法亦能分解质因数,假如出题人懒得造全质数的测试点,平均情况下的运行效率较高。
好像有定理指出,在区间 随机选择的正整数 的最大质因子期望渐进为 。
因此在期望意义下只会枚举到 的因数。
实践上,对于少量 以内的随机数,用这些方法分解质因数的效率是可以接受的。
筛法
上文中提到了筛法,但是没有给出实现。
1 2 3 4
for (size_t i = 2; i * i <= N; ++i) if (!vis[i]) for (size_t j = i * i; j <= N; j += i) vis[j] = true;
这种方法的理论渐进复杂度为 的,但在 OI 常见数据范围内,通常效率比 的欧拉筛来的快。
给出线性筛的常见写法:
1 2 3 4 5 6 7 8 9 10 11
for (size_t i = 2; i <= N; ++i) { if (!vis[i]) prime.push_back(i); for (size_t j : i) { if (i * j > N) break; vis[i * j] = true; if (i % j == 0) break; } }
boolmillerRabin(int n){ if (n < 3 || !(n & 1)) return n == 2; if (n % 3 == 0) return n == 3; int u = n - 1, t = 0; while (!(u & 1)) u >>=1, ++t; for (int i = 0; i < test_time; ++i) { int a = rand() % (n - 3) + 2, v = quickPow(a, u, n); if (v == 1) continue; int s; for (s = 0; s < t; ++s) { if (v == n - 1) break; v = (longlong)v * v % n; } if (s == t) returnfalse; } returntrue; }