C++质因数分解实战:如何用O(logn)算法快速计算因子个数(附LeetCode真题解析)
在算法竞赛和紧张的面试环节中,计算一个整数的因子个数是一个经典且高频的问题。很多开发者第一反应是使用传统的遍历方法,从1到sqrt(n)逐个检查,这在处理小数据时固然可行。然而,当面对大整数,或者需要在短时间内处理海量数据时,O(√n)的时间复杂度立刻成为性能瓶颈,导致程序超时,让你在机试或笔试中痛失分数。我自己就曾在类似的场景下吃过亏,看着20%的通过率百思不得其解,直到掌握了质因数分解这把“钥匙”。
实际上,计算因子个数与质因数分解有着深刻而优雅的联系。一旦我们将一个数分解为质因数的幂次乘积,因子个数问题便从一个需要遍历的“搜索问题”,转化为了一个基于组合数学的“计算问题”。这种思路的转变,带来的往往是数量级上的效率提升。本文将深入探讨如何利用质因数分解,实现接近O(log n)时间复杂度的因子计数算法,并提供可直接用于实战的C++代码模板。我们不仅会剖析其背后的数学原理,还会结合LeetCode真题进行演练,让你在下次遇到类似问题时,能够游刃有余。
1. 从朴素方法到数学洞察:为何需要质因数分解?
在开始学习高效的算法之前,我们有必要彻底理解传统方法的局限性,以及新方法优越性的根源。这能帮助我们在面对新问题时,知道该从哪个方向去思考和优化。
1.1 传统方法的瓶颈与复杂度分析
最直观的因子计数方法是遍历所有可能的候选数。对于一个给定的正整数 n,其因子总是成对出现的(除了完全平方数的平方根)。例如,12的因子对是(1,12), (2,6), (3,4)。因此,我们只需要从1遍历到 sqrt(n) 即可。
// 朴素方法:时间复杂度 O(√n)
int countFactors_naive(int n) {
int count = 0;
for (int i = 1; i * i <= n; ++i) {
if (n % i == 0) {
count++; // 计数 i
if (i != n / i) {
count++; // 计数 n/i
}
}
}
return count;
}
这个方法简单易懂,但其时间复杂度为 O(√n)。让我们看一个具体的性能对比:
| 输入数值 (n) | 循环次数 (≈√n) | 在1GHz CPU上的近似耗时 |
|---|---|---|
| 10^6 | 1000 | 微秒级 |
| 10^12 | 10^6 | 毫秒级 |
| 10^18 | 10^9 | 秒级 |
注意:在算法竞赛中,通常认为C++在1秒内能执行约10^8次基本操作。当n达到10^16时,√n = 10^8,已接近时间极限。若题目需要处理多个这样的大数,朴素方法必然超时。
1.2 质因数分解提供的全新视角
质因数分解定理(算术基本定理)指出,任何一个大于1的整数,都可以唯一地分解为一系列质数的幂的乘积。
例如:
108 = 2^2 × 3^3360 = 2^3 × 3^2 × 5^1
这个分解形式揭示了一个数的“DNA”。关键在于,一个数的所有因子,都可以通过从各个质因子的幂次中做出选择来构造。
以108为例:
- 对于质因子2,指数是2。在构造因子时,我们可以选择使用2^0, 2^1, 或2^2。共有 3 种选择 (指数+1)。
- 对于质因子3,指数是3。我们可以选择使用3^0, 3^1, 3^2, 或3^3。共有 4 种选择 (指数+1)。
由于每个质因子的选择是独立的,根据乘法原理,总的因子个数就是各质因子可选方案数的乘积: 因子总数 = (2+1) × (3+1) = 3 × 4 = 12
这个公式就是整个高效算法的核心。它将一个需要遍历的O(√n)问题,转化为了一个先分解、后计算的O(log n)问题。分解过程本身可能比O(log n)稍慢,但相比O(√n)已是巨大的飞跃。
2. 核心算法拆解:高效的质因数分解与因子计数实现
理解了数学原理,接下来我们关注如何在C++中高效地实现它。我们将分步构建一个健壮、高效的函数。

2099

被折叠的 条评论
为什么被折叠?



