分解质因数

单个数分解质因数

算法原理
采用试除法分解质因数即可
时间复杂度
\(O(\sqrt{n})\)\(O(\frac{\sqrt{n}}{ln(\sqrt{n})})\)

阶乘分解质因数

给定整数 \(N\),试把阶乘 \(N!\) 分解质因数,按照算术基本定理的形式输出分解结果中的 \(p_i\)\(c_i\) 即可。
5
2 3
3 1
5 1

一般方法分析

如果使用单个数分解质因数的方法,时间复杂度粗略估计为\(O(n \sqrt{n})\),对于n的1e6的数据范围,大概率会超时

#include <iostream>
#include <algorithm>

using namespace std;

const int N = 1e6 + 10;

int primes_cnt[N];

void divide(int n)
{
    for (int i = 2; i <= n / i; ++ i)
    {
        if (n % i == 0)
        {
            int cnt = 0;
            while (n % i == 0)
            {
                ++ cnt;
                n /= i;
            }
            
            primes_cnt[i] += cnt;
        }
    }
    
    if (n > 1) ++ primes_cnt[n];
}
int main()
{
    int n;
    cin >> n;
    
    for (int i = 2; i <= n; ++ i) divide(i);
    
    for (int i = 2; i <= n; ++ i) 
        if (primes_cnt[i] > 0) cout << i << ' ' << primes_cnt[i] << endl;
        
    return 0;
}

如果从优化的角度思考,如果能将divide中求cnt的过程进行预处理,快速求出n中质因子i的个数,复杂度会降低一些。
但实际考虑到预处理\(log_in\)的时间复杂度为\(O(n)\),并且由于n的变化,每次都需要预处理,这样预处理就没有什么意义了。

正解分析

在一般方法中,我们的思考方向是对于每个数都进行一次分解质因数
如果换种思考方向就是对于每一个质数,计算在阶乘中总共有多少个

算法原理
首先说明,上述的新的思考方向理论上是正确的,其可行性体现在以下的性质中

  • 性质1:\(n!\) 中 质因子 \(p\) 的个数为 \(\lfloor \frac{n}{p} \rfloor \ + \ \lfloor \frac{n}{p^2} \rfloor \ + \ ... \ + \ \lfloor \frac{n}{p^k} \rfloor\).(该性质的证明位于 质因数分解计算组合数)
    使用该公式,对于每个质数而言,需要从\(\lfloor \frac{n}{p} \rfloor\)\(\lfloor \frac{n}{p^k} \rfloor (p^k \leq n , p^{k + 1} > n)\),粗略计算为\(log_pn\)
    而1到a中的质数个数为\(\frac{n}{logn}\),所以粗略计算该算法的时间复杂度为\(O(n)\)

流程

  1. 首先使用线性筛筛取可能范围内的质数
  2. 利用上述公式分别计算每个质数的个数

代码实现

#include <iostream>

using namespace std;

const int N = 1e6 + 10;

int primes[N], cnt, st[N];

void init(int n)
{
    for (int i = 2; i <= n; ++ i)
    {
        if (!st[i]) primes[cnt ++] = i;
        for (int j = 0; primes[j] * i <= n; ++ j)
        {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}
int main()
{
    int n;
    cin >> n;
    
    init(n);
    
    for (int i = 0; i < cnt; ++ i)
    {
        int p = primes[i], cnt = 0;
        for (int j = n; j; j /= p) cnt += j / p; // 使用while还需要定义临时变量,不如for简洁
        cout << p << ' ' << cnt << endl; // 不需要判断cnt是否为0,因为至少会是1,因为n!中一定包含小于n的质数,这些质数的质因子就是他们自身,所以所有的质数的指数最小也会是1
    }
    
    return 0;
}
posted @ 2021-03-11 20:35  0x7F  阅读(520)  评论(0编辑  收藏  举报