一、单个数质因数分解

直接上代码:

#include <bits/stdc++.h>

using namespace std;

/**
 * 功能:分解质数因数
 * @param a 待分解的数字
 */
const int N = 1010;
int primes[N];              //质数因子数组
int idx;                    //质数因子数组下标游标
int mc[N];                  //mc对应着幂次
void Decomposition(int a) {
    //清空,防止重复
    memset(primes, 0, sizeof primes);
    memset(mc, 0, sizeof mc);
    idx = 0;
    //开始
    for (int i = 2; i * i <= a; i++) {
        //如果发现a的一个因子i
        if (a % i == 0) {
            primes[++idx] = i;      //质因子数组维护
            mc[idx] = 1;            //指数幂次数+1
            a /= i;                 //去掉这个因子
            while (a % i == 0) {    //除干净它
                mc[idx]++;          //指数幂次数+1
                a /= i;             //继续缩小
            }
        }
    }
    //可能剩余一个很大的质因子数
    if (a > 1) {
        primes[++idx] = a;
        mc[idx] = 1;
    }
}

int main() {
    //求2~100区间内所有数字的质因子
    for (int i = 2; i <= 100; i++) {
        cout << i << ":     ";
        //分解质因子
        Decomposition(i);
        //遍历输出质因子
        for (int j = 1; j <= idx; j++) {         //遍历每个质因子
            for (int k = 1; k <= mc[j]; k++) {  //遍历质因子的个数
                cout << primes[j];
                //最后一个因子,并且是最后一个因子的最后一个输出,后面不加*
                if (j == idx && k == mc[j]) continue;
                cout << "*";
            }
        }
        cout << endl;
    }
    return 0;
}

适用场景:单个数字的质因数分解。如果是区间内质因数分解,这个办法就太慢了。

二、区间内质因数分解

1、朴素版本

思路:

1、先 get_primes(sqrt(r))取得所有\(r\)的所有可能小质数因子(采用欧拉筛,即线筛)。

2、双重循环:外层循环遍历\([l,r]\)之间的每个数字,内层循环遍历筛出来的每一个质数因子,如果能被整除掉,就一直除,直到不再包含这个质数因子。

3、除完已知小质数列表后,\(i\)有两种可能 ,第一种\(i=1\),表示被所有小质数因数除干净了;另一种是\(i>1\),表示还存在一个大于\(sqrt(x)\)的大质数因数。将找到的所有可能质数因子记录到一个vector<int> v[N]中,

附:C++实现质因子分解的理论基础

优点:
因为采用了先穷举根号内质数因数的办法,所以去掉了很多无用的尝试,对比传统暴力的分解质因数方法

#include <bits/stdc++.h>

using namespace std;
const int INF = 0x3f3f3f3f;

typedef long long LL;
LL l = 1, r = 2000000;

const int N = 1e7 + 10;
//欧拉筛[线性筛法]
int primes[N], cnt;     // primes[]存储所有素数
bool st[N];             // st[x]存储x是否被筛掉
void get_primes(int n) {
    for (int i = 2; i <= n; i++) {
        if (!st[i]) primes[cnt++] = i;
        for (int j = 0; primes[j] <= n / i; j++) {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}

vector<int> v[N];

int main() {
    /**=====================================================***/
    //计时开始
    clock_t startTime= clock();
    /**=====================================================***/

    //1、筛出小于sqrt(r)的所有质数
    get_primes(sqrt(r));

    //2、遍历每个数字,求质因子
    for (LL i = l; i <= r; i++) {
        int t = i;
        for (int j = 0; j < cnt && primes[j] * primes[j] <= i; j++) {
            if (i % primes[j] == 0) {
                v[i].push_back(primes[j]);
                //除干净这个质数因子
                while (t % primes[j] == 0) t /= primes[j];
            }
        }
        if (t > 1) v[i].push_back(t);
    }

    //输出质数因子有哪些
    for (int i = l; i <= r; i++) {
        cout << i << ":       ";
        for (auto c:v[i]) cout << c << " ";
        cout << endl;
    }
    cout << endl;

    /**************************************************************/
    //计时结束
    clock_t endTime = clock();
    cout << "The run time is: " <<(double)(endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;
    /**=====================================================***/
    //The run time is: 4.273s
    return 0;
}

2、优化版本

前导知识:整数除法向上取整

在前面基础上进行优化。
原理:上面是双重循环,遍历每一个数字求质数因子。 这里是遍历所有质数因子,成倍的筛出质数的倍数,是单层循环,速度当然更快,类似于埃筛的思路。

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
LL l = 1, r = 2000000;

const int N = 1e7 + 10;
//欧拉筛[线性筛法]
int primes[N], cnt;     // primes[]存储所有素数
bool st[N];             // st[x]存储x是否被筛掉
void get_primes(int n) {
    for (int i = 2; i <= n; i++) {
        if (!st[i]) primes[cnt++] = i;
        for (int j = 0; primes[j] <= n / i; j++) {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) break;
        }
    }
}

vector<int> v[N];

int main() {
    /**=====================================================***/
    //计时开始
    clock_t startTime = clock();
    /**=====================================================***/
    //1、筛出小于sqrt(r)的所有质数
    get_primes(sqrt(r));

    //遍历每个小质数因子
    for (int i = 0; i < cnt; i++) {
        int p = primes[i];
        //1、利用数组下标的位移,记录数据
        //2、找到大于l的第一个p的倍数,然后,每次增加p,相当于找出p的整数倍
        for (LL j = ((l - 1) / p + 1) * p; j <= r; j += p)
            //在每个倍数中记录下这个共同的质数因子p,比如 6里面包含2,8里面包含2,10里面包含2,相当于埃筛一次找到所有p的整数倍
            v[j - l].push_back(p); //标识数组vec[j-l],也就是真实的l这个数字中,存在p这个因子。
    }

    //如果还存在大的质数因子
    for (LL i = l; i <= r; i++) {
        LL tmp = i; //将i拷贝出来给了tmp,tmp要不断的减少啦,而i要保留。

        //遍历每个已经找到的因子
        for (int p: v[i - l]) {
            //除干净
            while ((tmp % p) == 0) tmp /= p;
        }
        //如果还存在大的质数因子
        if (tmp > 1)v[i - l].push_back(tmp);
    }

    //输出质数因子有哪些
    for (int i = l; i <= r; i++) {
        cout << i << ":       ";
        for (auto c:v[i - l]) cout << c << " ";
        cout << endl;
    }
    cout << endl;

    /**************************************************************/
    //计时结束
    clock_t endTime = clock();
    cout << "The run time is: " << (double) (endTime - startTime) / CLOCKS_PER_SEC << "s" << endl;
    /**=====================================================***/
    //第一种方法用时:
    //The run time is: 4.273s

    //第二种方法用时:
    //The run time is: 1.677s
    return 0;
}

实测性能提高一倍以上。