快速计算约数的个数——从基础到高级

简介: 快速计算约数的个数——从基础到高级

题目来源:【欧拉计划第 12 题】 高度可除的三角数 Highly divisible triangular number

这道题我们在枚举完三角数后,最重要的是去判断何时某个三角数约数的个数大于 500

下面我们来看下,针对计算约数的个数问题,用不同的算法解决,逐步求得最优解

方法 1

最简单,更是非常容易理解的方法

复杂度:O ( n 2 ) \large O_{\left ( n^{2} \right )}O(n2)

主要思想:定义变量,使其在小于传入判断值的条件下从 1 开始自增,如果判断值和该变量进行模取运算后的值为 0,则说明该变量此时的值是判断值得一个约数。那么,程序计数器自增,记录下此值。循环结束后,输出计数器保存的值即为判断值约数的个数

这种方法优点除易于理解外,怕是没有优点了。缺点当然就是时间复杂度太高,一个值就需要去从 1 一直判断到该值。试想,如果数据量呈指数增长,这种方法恐怕在一般的计算机上不容易很快得到答案

实现代码如下

int check(long long n)
{
    int count = 0;
    long long i = 1; //注意数据范围
    while (i <= n)
    {
        if (n % i == 0) //成立,这说明此时 i 为 n 的一个约数
        {
            count++; //计数器自增
        }
        i++; //继续判断下一个数字是否为 i 的约数
    }
    return count;
}

方法 2

复杂度:O ( n ) \large O_{\left ( \sqrt{n} \right )}O(n)

主要思想:对比可以看出,方法一和方法二差别不大,但影响最关键的是它们的复杂度,直接由 O ( n 2 ) O_{\left ( n^{2} \right )}O(n2) 降至 O ( n ) O_{\left ( \sqrt{n} \right )}O(n)

同样,仍然是暴力枚举,只不过这里的判断条件由 i < = n 变为 i * i < = n,复杂度优化了些许。进入 for() 循环后,如果 n % i == 0 ,那么说明此时的 i 值是 n 的一个约数

大家在这里要注意的是 if...else 语句内容,这里主要解释下此处和方法一的差别

举个例子,如果 n = 4i = 2,满足 2 × 2 = 4 的条件,但此时 4 的两个约数 2 相当于一个,程序计数器只能自增 1 ,而不是 2

当然,如果进入 for() 循环后,不满足条件 i * i = n ,那么自然是两个不同的约数,此时程序计数器需要增加 2,而不是 1

int check(long long n)
{
    int count = 0;
    for (long long i = 1; i * i <= n; i++)
    {
        if (n % i == 0)
        {
            if (i * i == n) // 区别所在,重点理解
                count++;
            else
                count += 2;
        }
    }
    return count;
}

方法 3

试除法求解

vector<int> get_divisors(int n)
{
    vector<int> res;
    for (int i = 1; i <= n / i; i++)
        if (n % i == 0)
        {
            res.push_back(i);
            if (i != n / i)
                res.push_back(n / i);
        }
    sort(res.begin(), res.end());
    return res;
}

LeetCode 题解思路

方法四

约数个数定理

设一个数可以表示为其素数幂的乘积,即:

n = p 1 e 1 ⋅ p 2 e 2 ⋅ ⋅ ⋅ p m e m \large n={p_{1}}^{e_{1}} \cdot {p_{2}}^{e_{2}} \cdot\cdot\cdot {p_{m}}^{e_{m}}n=p1e1p2e2pmem

则该数的约数个数为:

( e 1 + 1 ) ⋅ ( e 2 + 1 ) ⋅ ⋅ ⋅ ( e m + 1 ) \large \left ( e_{1}+1 \right )\cdot \left ( e_{2}+1 \right )\cdot \cdot \cdot \left ( e_{m}+1 \right )(e1+1)(e2+1)(em+1)

参考代码:

#include <bits/stdc++.h>
using namespace std;
int main()
{
    int N, n, i, s, r;
    while (scanf("%d", &N) != EOF)
    {
        while (N--)
        {
            cin >> n;
            s = 1;
            for (i = 2; i * i <= n; i++)
            {
                r = 0;
                while (n % i == 0)
                {
                    r++;
                    n /= i;
                }
                if (r > 0)
                {
                    r++;
                    s *= r;
                }
            }
            if (n > 1)
                s *= 2;
            cout << s;
        }
    }
    return 0;
}



相关文章
|
6月前
|
Python
一个大于1的自然数,除了1和它本身外,不能被
一个大于1的自然数,除了1和它本身外,不能被
|
6月前
|
算法 搜索推荐 程序员
第五十练 请以递归方式实现计算给定数字的幂的函数
第五十练 请以递归方式实现计算给定数字的幂的函数
28 4
|
6月前
每日一题(最大连续1的个数,完全数计算)
每日一题(最大连续1的个数,完全数计算)
32 0
|
6月前
|
算法 搜索推荐 程序员
第四十七练 请以递归方式实现计算整数列表的最大值
第四十七练 请以递归方式实现计算整数列表的最大值
45 2
|
6月前
|
机器学习/深度学习 存储 算法
数据结构与算法面试题:给定非负整数 m 和 n,计算不大于 m 的数字中,素数的个数。(提示:算法原理为埃氏筛、线性筛)
数据结构与算法面试题:给定非负整数 m 和 n,计算不大于 m 的数字中,素数的个数。(提示:算法原理为埃氏筛、线性筛)
88 0
数列中,第一项为3,后一项都比前一项的值增5。下列给定程序中,函数fun的功 能是:计算前n(4≤n≤50)项的累计和。在累加过程中把那些被4除后余2的当前累 加值放入数组中
数列中,第一项为3,后一项都比前一项的值增5。下列给定程序中,函数fun的功 能是:计算前n(4≤n≤50)项的累计和。在累加过程中把那些被4除后余2的当前累 加值放入数组中
|
存储 算法 前端开发
前端算法-除自身外数组的乘积
前端算法-除自身外数组的乘积
判断一个数是否为4的整数次幂(2的升级版--双份快乐)
判断一个数是否为4的整数次幂(2的升级版--双份快乐)
|
算法
求两个数对应二进制位不同的个数(深度剖析+补充例题)
求两个数对应二进制位不同的个数(深度剖析+补充例题)
169 0
求两个数对应二进制位不同的个数(深度剖析+补充例题)
6-10 阶乘计算升级版 (20 分)
6-10 阶乘计算升级版 (20 分)
116 0