跳转至

质因子分解

唯一分解定理

对于任意一个大于 $1$ 的正整数 $n$,都可以唯一地分解为若干个质数的乘积,形式如下:

\[ n = p_1^{a_1} \times p_2^{a_2} \times \cdots \times p_k^{a_k},\quad p_i \in \text{prime},\ a_i \in \mathbb{Z}^+ \]

例如:

  • $12 = 2^2 \times 3^1$
  • $9 = 3^2$
  • $5 = 5^1$

这一性质被称为唯一分解定理,是数论中的基石。


朴素质因子分解方法

思路

  • 从 $2$ 开始枚举每个整数 $i$,尝试整除 $n$。
  • 每当 $i \mid n$,就连续除尽 $n$ 中的所有 $i$,统计次数。
  • 枚举只需要到 $\sqrt{n}$,因为若 $n$ 有大于 $\sqrt{n}$ 的因子,另一个因子必定小于它。
  • 最后如果 $n > 1$,说明剩下的 $n$ 本身是一个质数。

实现

for (int i = 2; i * i <= n; i++) // 注意 i * i 是否溢出 int
{
    if (n % i == 0)
    {
        int cnt = 0;
        while (n % i == 0)
        {
            n /= i;
            cnt++;
        }
        if (cnt) cout << i << " " << cnt << "\n";
    }
}
if (n != 1)
{
    cout << n << " 1\n"; // 剩余一个大质因子
}

时间复杂度:$O(\sqrt{n})$


质因子分解的应用

约数个数定理

若 $n = p_1^{a_1} \times p_2^{a_2} \times \cdots \times p_k^{a_k}$,则 $n$ 的正整数约数个数为:

\[ \text{d}(n) = (a_1+1)(a_2+1)\cdots(a_k+1) = \prod_{i=1}^k (a_i+1) \]

原理:每个质因子 $p_i$ 可以选 $0$ 到 $a_i$ 次,共 $a_i + 1$ 种选法,乘法原理得总数。

约数和定理

\[ \text{s}(n) = (p_1^0 + p_1^1 + \cdots + p_1^{a_1}) \times \cdots \times (p_k^0 + p_k^1 + \cdots + p_k^{a_k}) \]

等比数列求和可化简为:

\[ \text{s}(n) = \prod_{i=1}^k \frac{p_i^{a_i+1} - 1}{p_i - 1} \]

常见技巧与应用

最大公约数(GCD)的质因子分析

若:

  • $a = p_1^{x_1}\cdot p_2^{x_2}\cdot \cdots$
  • $b = p_1^{y_1}\cdot p_2^{y_2}\cdot \cdots$

则:

\[ \gcd(a,b) = p_1^{\min(x_1, y_1)} p_2^{\min(x_2, y_2)} \cdots \]

最小公倍数(LCM)的质因子分析

\[ \text{lcm}(a,b) = p_1^{\max(x_1, y_1)} p_2^{\max(x_2, y_2)} \cdots \]

这些方法同样适用于多个数字的 GCD / LCM 计算。


快速质因子分解

场景

当需要对大量不大的数进行质因子分解时,可以先筛出每个数的最小质因子 $f[i]$,再在对数时间内完成分解。

实现思路

  • 使用欧拉筛埃氏筛预处理每个 $i$ 的最小质因子。
  • 对于任意 $n$,只需不断除以 $f[n]$,直到 $n = 1$,即可完成分解。

代码实现

int f[N], p[N], cnt;
bool vis[N];

void prime(int n)
{
    vis[0] = vis[1] = true;
    for (int i = 2; i <= n; i++)
    {
        if (!vis[i])
        {
            p[++cnt] = i;
            f[i] = i;
        }
        for (int j = 1; j <= cnt && i * p[j] <= n; j++)
        {
            vis[i * p[j]] = true;
            f[i * p[j]] = p[j];
            if (i % p[j] == 0) break;
        }
    }
}

void factorize(int n)
{
    map<int, int> cnt;
    while (n != 1)
    {
        cnt[f[n]]++;
        n /= f[n]; // 除以自己的最小质因子,快速分解
    }
    for (auto it : cnt)
        cout << it.first << "^" << it.second << " ";
    cout << "\n";
}

时间复杂度

  • 筛预处理:$O(n)$
  • 单次分解:$O(\log n)$

应用

适用于海量数据处理,如分解 $10^6$ 个 $\leq 10^8$ 的数。