如何以最低时间复杂度分解10^18以内整数的质因数?
针对大整数(1 ≤ n ≤ 10^18)质因数分解的优化方案
你的当前方案(埃氏筛预处理小质数+Pollard Rho分解大因子)已经是处理超大整数分解的主流思路了,但确实还有不少可以提速和增强正确性的优化点,我来给你拆解一下:
核心优化方向
- 替换素性判断逻辑:你现在对Pollard Rho分解出的因子直接当作质数处理,这是有风险的——Pollard Rho可能返回合数,必须先做素性测试才能确定。这里用Miller-Rabin强伪素数测试是最优选择,对于64位整数,用固定的几个底数就能保证100%准确,完全不需要依赖预处理的质数数组判断素性。
- 优化Pollard Rho的实现细节:
- 不要每次调用
pollardrho都重新初始化随机数种子,程序启动时初始化一次就够了,避免频繁调用time(NULL)带来的开销和随机性波动。 - 使用
__int128实现无溢出的乘法,避免普通ull相乘溢出导致的计算错误,同时比二进制乘法更快。 - 调整循环终止条件和迭代逻辑,减少不必要的计算。
- 不要每次调用
- 简化预处理质数的范围:预处理到1e6就足够覆盖大部分小因子了,更大的小因子用Pollard Rho分解效率不会差,还能减少初始化时间和内存占用。
- 优化因子收集流程:统一用素性测试判断每个分解出的数,避免重复判断,最后排序输出结果更规范。
优化后的完整代码
#include <iostream> #include <vector> #include <cstdlib> #include <algorithm> #include <ctime> #include <cmath> using namespace std; typedef unsigned long long ull; typedef pair<ull, int> pui; // 用__int128实现无溢出乘法取模 ull mul_mod(ull a, ull b, ull mod) { return (ull)((__int128)a * b % mod); } // 快速幂取模 ull pow_mod(ull base, ull exp, ull mod) { ull ret = 1; base %= mod; while (exp > 0) { if (exp & 1) ret = mul_mod(ret, base, mod); base = mul_mod(base, base, mod); exp >>= 1; } return ret; } // Miller-Rabin素性测试,针对64位整数的固定底数,保证100%准确 bool is_prime(ull n) { if (n <= 1) return false; if (n <= 3) return true; if (n % 2 == 0) return false; // 分解n-1 = d*2^s ull d = n - 1; int s = 0; while (d % 2 == 0) { d /= 2; s++; } // 用于64位整数的测试底数集合 vector<ull> bases = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37}; for (ull a : bases) { if (a >= n) continue; ull x = pow_mod(a, d, n); if (x == 1 || x == n - 1) continue; bool composite = true; for (int j = 1; j < s; j++) { x = mul_mod(x, x, n); if (x == n - 1) { composite = false; break; } } if (composite) return false; } return true; } // Pollard Rho算法找因子 ull pollard_rho(ull n) { if (n % 2 == 0) return 2; if (n % 3 == 0) return 3; if (n % 5 == 0) return 5; while (true) { ull c = rand() % (n - 1) + 1; auto f = [&](ull x) { return (mul_mod(x, x, n) + c) % n; }; ull x = 2, y = 2, d = 1; int steps = 0, max_steps = 1 << 20; while (d == 1 && steps < max_steps) { x = f(x); y = f(f(y)); d = __gcd(abs((long long)(x - y)), n); steps++; } if (d != 1 && d != n) return d; } } // 递归分解n,收集所有质因子 void factorize(ull n, vector<ull>& factors) { if (n == 1) return; if (is_prime(n)) { factors.push_back(n); return; } ull d = pollard_rho(n); factorize(d, factors); factorize(n / d, factors); } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); srand(time(nullptr)); // 只初始化一次随机数种子 ull n; cin >> n; vector<ull> factors; // 预处理1e6以内的质数快速除尽小因子 vector<bool> is_small_prime(1000001, true); is_small_prime[0] = is_small_prime[1] = false; for (ull i = 2; i <= 1000000; i++) { if (is_small_prime[i]) { while (n % i == 0) { factors.push_back(i); n /= i; } if (i * i <= 1000000) { for (ull j = i * i; j <= 1000000; j += i) { is_small_prime[j] = false; } } } } if (n > 1) { factorize(n, factors); } // 统计每个质因子的次数并排序输出 sort(factors.begin(), factors.end()); vector<pui> result; for (ull p : factors) { if (result.empty() || result.back().first != p) { result.emplace_back(p, 1); } else { result.back().second++; } } // 输出结果(可根据需求调整格式) for (size_t i = 0; i < result.size(); i++) { if (i > 0) cout << " * "; cout << result[i].first; if (result[i].second > 1) cout << "^" << result[i].second; } cout << endl; return 0; }
为什么这个版本更快更可靠?
- Miller-Rabin的准确性:用固定的12个底数,确保所有≤2^64的数都能被正确判断素性,完全不会出现误判。
- 无溢出乘法:用
__int128实现乘法取模,比普通的溢出后处理快很多,也避免了计算错误。 - Pollard Rho的效率提升:调整了迭代逻辑,减少了不必要的
gcd调用,随机数种子只初始化一次,避免了频繁系统调用的开销。 - 灵活的预处理:预处理1e6的质数足够快速处理小因子,就算省略这一步直接用
factorize,整体效率也不会差,但预处理能减少Pollard Rho的调用次数。
内容的提问来源于stack exchange,提问作者艩tef
相关产品推荐
相关产品推荐

