简单数论——约数

约数

算术基本定理

对于任何一个大于1的自然数N,如果N不为质数,那么都可以唯一分解成有限个质数的乘积N = \({P_1}^{\alpha_1} {P_2}^{\alpha_2} ... {P_n}^{\alpha_n}\)

这里\(P_1<P_2<...<P_n\)且均为质数,其诸指数\(a_i\)是正整数。这样的分解称为N的标准分解式

试除法求一个数所有约数

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

求约数个数 O\((n \sqrt n)\)

对任意一个数N都可以写成:

N = \({P_1} ^ {\alpha_1} {P_2} ^ {\alpha_2} ... {P_k} ^ {\alpha_k}\)

对于他的任一约数d = \({P_1} ^ {\beta_1} {P_2} ^ {\beta_2} ... {P_k} ^ {\beta_k}\)\((0 \le \beta_i \le \alpha_i)\)只要指数\(\beta\)不同,d就不同

所以N的约数的个数实际就是\(\beta_1\) ~ \(\beta_k\)的取法个数,其实就是看\(\beta_k\)有多少组合方式,如\(\beta_k\)有(0~\(\alpha_k\))即\((\alpha_k + 1)\)种选法

整体N的选法即N的约数个数 = \((\alpha_1 + 1) (\alpha_2 + 1) ... (\alpha_k+1)\)

代码思路

我们只需要将N进行分解质因式就可以了,那么如何分解质因式呢?

如求\((a_1 a_2 ... a_n)\)的质因式分解结果

\(a_1,a_2,...,a_n\)都分别分解,再把每一个数的指数累加到一起就可以了

其实就是设置一个映射,\(P_k\)是底数,他的指数++,

假设先求出\(a_1\)的某一个质因子\(P_i ^ {\alpha_i}\),可以用一个Hash表或一个map将他存下来

如map[\(P_i\)] += \(\alpha_i\)就可以了,最后map就存储了整个乘积的指数和底数

约数个数就是所有指数+1在相乘

/**
 * 给定n个数a_1到a_n,
 * 求出a_1 * a_2 * ... *a_n的约数个数取模1e9+7
 */
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <vector>

using namespace std;

typedef long long LL;

const int N = 110, mod = 1e9 + 7;

int main()
{
    int n;
    cin >> n;

    unordered_map<int, int> primes;

    while (n -- )
    {
        int x;
        cin >> x;

        for (int i = 2; i <= x / i; i ++ )
            while (x % i == 0)
            {
                x /= i;
                primes[i] ++ ;
            }

        if (x > 1) primes[x] ++ ; // 说明x是一个比较大的之前没出现过的质因数,如7,把他记录一下
    }

    LL res = 1;
    for (auto p : primes) res = res * (p.second + 1) % mod;

    cout << res << endl;

    return 0;
}

求约数之和

N = \({P_1} ^ {\alpha_1} {P_2} ^ {\alpha_2} ... {P_k} ^ {\alpha_k}\)

N的任一约数d = \({P_1} ^ {\beta_1} {P_2} ^ {\beta_2} ... {P_k} ^ {\beta_k}\)\((0 \le \beta_i \le \alpha_i)\),约数之和不就是所有的d加在一起嘛

约数之和 = \((P_1^0 + P_1 ^ 1 + ... + P_1 ^ {\alpha_1}) (P_2^0 + P_2 ^ 1 + ... + P_2 ^ {\alpha_2}) ... (P_k^0 + P_k ^ 1 + ... + P_k ^ {\alpha_k})\)

将上述式子用乘法分配律展开就是N的各个d的取值,一共有\((\alpha_1+1)(\alpha_2+1)...(\alpha_k+1)\)种选法

上述每一个展开的乘积都是\((P_1^{\beta_1} P_2^{\beta_2}...P_k^{\beta_k})\)的形式,\((0 \le \beta_k \le \alpha_k)\)

这里的每一项其实都是一个约数,所以他一共的个数就是约 数的个数,可以发现这个式子展开其实就是把所有约数加到一起了

代码思路

唯一的难点就是如何求出\(P^0 + P^1 + ... P^\alpha\)呢?

方法有很多,如可以初始令t = 1, 每次令t = p*t+1

第一次执行后t=\(p^2+p+1\)

执行a次后t=\(P^0 + P^1 + ... P^\alpha\)

/**
 * 给定n个数a_1到a_n,
 * 求出a_1 * a_2 * ... *a_n的约数之和取模1e9+7
 */
#include <iostream>
#include <algorithm>
#include <unordered_map>
#include <vector>

using namespace std;

typedef long long LL;

const int N = 110, mod = 1e9 + 7;

int main()
{
    int n;
    cin >> n;

    unordered_map<int, int> primes;

    while (n -- )
    {
        int x;
        cin >> x;

        for (int i = 2; i <= x / i; i ++ )
            while (x % i == 0)
            {
                x /= i;
                primes[i] ++ ;
            }

        if (x > 1) primes[x] ++ ;
    }

    LL res = 1;
    for (auto p : primes) // 枚举所有的质因数
    {
        LL a = p.first, b = p.second; // p是底数,a是指数
        LL t = 1;
        // 求出P^0+P^1+...+P^n
        while (b -- ) t = (t * a + 1) % mod;
        res = res * t % mod; 
    }

    cout << res << endl;

    return 0;
}

最大公约数——欧几里得算法/辗转相除法

整除的概念

若整数b除以非零整数a,商为整数,且余数为零, 我们就说b能被a整除,或说a能整除b,写做a|b。

若d|a,且d|b,则d|(a+b),且d|(\(\theta a + \beta b)\))即任意倍数的a+任意倍数的b

例子:
a=20, b=30, d=10,

d|a=\(20 \div 10\)=2,

d|b=\(30 \div 10\)=3,

d|(a+b)=\((20+30)\div10\)=5

(a,b)的最大公约数 = (b, a mod b)

a mod b = a-(a//b)*b

a//b是一个整数c,所有a mod b = a - c*b

例子:
a = 10, b = 3,

10 mod 3 = 1,

a - (a//b)b = 10 - (10//3)3 = 10-3*3 = 1

(a,b) = (b, a mod b)

对于左面,d|a,d|b,

对于右面,d | b, d | (a-c*b),因为d | b,所以d | (a - c*b + c*b),所以d | a

所以右边的任何一个公约数都是左面的一个公约数,左面也同理,所以这两个公约数集合是相同的

所以左边的最大公约数就等于右面的最大公约数

通俗来讲:

gcd(a,b) = gcd(b, a mod b), (gcd : greatest common divisor最大公约数)

如求gcd(1997, 615)具体步骤如下:

  • 1997 / 615 = 3 ... 152
  • 615 / 152 = 4 ... 7
  • 152 / 7 = 21 ...5
  • 7 / 5 = 1 ... 2
  • 5 / 2 = 2 ... 1
  • 2 / 1 = 2 ... 0

实际上下一步计算就是gcd(b, a mod b)

posted @ 2020-10-30 22:26  晓尘  阅读(451)  评论(0)    收藏  举报