首页 > 编程语言 >算法学习笔记(34)——约数

算法学习笔记(34)——约数

时间:2022-12-10 09:46:57浏览次数:64  
标签:约数 gcd int cin sqrt 34 算法 include

约数

约数的定义

若整数 \(n\) 除以整数 \(d\) 的余数为 \(0\),即 \(d\) 能整除 \(n\),则称 \(d\) 是 \(n\) 的约数,\(n\) 是 \(d\) 的倍数,记为 \(d|n\)。

算数基本定理的推论

正约数集合

在算数基本定理中,若正整数 \(N\) 被唯一分解为 \(N = p_1^{c_1}p_2^{c_2}...p_m^{c_m}\),其中 \(c_i\) 都是正整数,\(p_i\) 都是质数,且满足 $p_1 < p_2 < ... < p_m $,则 \(N\) 的正约数集合可以写作:

\[\lbrace p_1^{c_1}p_2^{c_2}...p_m^{c_m} \rbrace,其中 0 \le b_i \le c_i \]

正约数个数

\[(c_1 + 1) * (c_2 + 1) * ... * (c_m + 1) = \prod_{i=1}^{m}(c_i + 1) \]

此处 \(1\) 代表质数 \(p_i\) 的 \(0\) 次方。

正约数之和

\[(1 + p_1 + p_1^2 + ... + p_1^{c_1}) * ... * (1 + p_m + p_m^2 + ... + p_m^{c_m}) = \prod_{i=1}^{m}(\sum_{j=0}^{c_i}(p_i)^j) \]

上式按照乘法多项式展开之后的每一项都是 \(N\) 的一个约数。

一、试除法求约数

如果 \(d > \sqrt{N}\) 是 \(N\) 的约数,那么 \(N/d \le \sqrt{N}\) 也是 \(N\) 的约数。换言之,约数总是成对出现的(对于完全平方的数字,\(\sqrt{N}\) 会单独出现一次)。

因此,只需扫描 \(1\) 到 \(\sqrt{N}\) 之间的数字,尝试能否整除 \(N\),若能整除,则 \(N/d\) 也是 \(N\) 的约数。

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

题目链接:AcWing 869. 试除法求约数

#include <iostream>
#include <vector>
#include <algorithm>

using namespace std;

void get_divisors(int n)
{
    // 用于存储约数
    vector<int> res;
    
    // 枚举从 1 到 sqrt(n) 的数
    for (int i = 1; i <= n / i; i ++ )
        // 如果 i 是 n 的约数
        if (n % i == 0) {
            // 将 i 存起来
            res.push_back(i);
            // 如果不是完全平方数,则将 n/i 也存起来
            if (i != n / i) res.push_back(n / i);
        }
    
    // 排序约数数组
    sort(res.begin(), res.end());
    
    for (auto i : res) cout << i << ' ';
    puts("");
}

int main()
{
    int n;
    cin >> n;
    while (n -- ) {
        int a;
        cin >> a;
        get_divisors(a);
    }
    return 0;
}

二、约数个数

利用算数基本定理的推论

题目链接:AcWing 870. 约数个数

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

#include <iostream>
#include <unordered_map>

using namespace std;

typedef long long LL;

const int MOD = 1e9 + 7;

int main()
{
    int n;
    cin >> n;
    
    // 采用哈希表存储每一个质因子对应出现的次数
    unordered_map<int, int> primes;
    while (n -- ) {
        int a;
        cin >> a;
        
        // 筛质数
        for (int i = 2; i <= a / i; i ++ )
            while (a % i == 0) {
                a /= i;
                primes[i] ++;
            }
        if (a > 1) primes[a] ++;
    }
    
    LL res = 1;
    // 代公式
    for (auto prime : primes) res = res * (prime.second + 1) % MOD;
    cout << res << endl;
    
    return 0;
}

三、约数之和

题目链接:AcWing 871. 约数之和

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

#include <iostream>
#include <unordered_map>

using namespace std;

const int MOD = 1e9 + 7;

typedef long long LL;

int main()
{
    int n;
    cin >> n;
    
    unordered_map<int, int> primes;
    while (n -- ) {
        int a;
        cin >> a;
        
        for (int i = 2; i <= a / i; i ++ )
            while (a % i == 0) {
                a /= i;
                primes[i] ++;
            }
        if (a > 1) primes[a] ++;
    }
    
    LL res = 1;
    for (auto prime : primes) {
        int p = prime.first, c = prime.second;
        LL t = 1;
        while (c -- ) t = (t * p + 1) % MOD;
        res = res * t % MOD;
    }
    cout << res << endl;
    
    return 0;
}

四、最大公约数

欧几里得算法

\[\forall a,b \in \mathbb{N}, b \neq 0, gcd(a,b)=gcd(b, a \bmod b) \]

证明:

  • 若 $ a \le b$,则 \(gcd(b, a \bmod b) = gcd(b,a) = gcd(a,b)\),显然成立。
  • 若 \(a \ge b\),不妨设 \(a = q * b + r\),其中 \(0 \le r < b\)。显然 \(r = a \bmod b\)。对于 \(a,b\) 的任意公约数 \(d\),因为 \(d|a\), \(d|q * b\),故 \(d|(a-qb)\) ,即 \(d|r\),因此 \(d\) 也是 \(b,r\) 的公约数。反之亦成立。故 \(a,b\) 的公约数集合与 \(b, a \bmod b\) 的公约数集合相同。于是他们的最大公约数自然相等。

时间复杂度:\(O(\log(a+b))\)

题目链接:AcWing 872. 最大公约数

#include <iostream>

using namespace std;

int gcd(int a, int b)
{
    // 0可以被任何数整除
    return b ? gcd(b, a % b) : a;
}

int main()
{
    int n;
    cin >> n;
    while (n -- ) {
        int a, b;
        cin >> a >> b;
        cout << gcd(a, b) << endl;
    }
    return 0;
}

也可调用STL的__gcd()函数:

#include <iostream>
#include <algorithm>

using namespace std;

int main()
{
    int n;
    cin >> n;
    while (n -- ) {
        int a, b;
        cin >> a >> b;
        cout << __gcd(a, b) << endl;
    }
    return 0;
}

欧几里得算法是最常用的求最大公约数的算法。不过,因为高精度除法(取模)不容易实现,需要做高精度运算时,可考虑用更相减损数代替欧几里得算法。

更相减损数

\[\forall a, b\in \mathbb{N}, a \geq b, 有 gcd(a,b) = gcd(b, a - b) = gcd(a, a - b) \\ \forall a, b\in \mathbb{N}, 有 gcd(2a, 2b) = 2gcd(a, b) \]

标签:约数,gcd,int,cin,sqrt,34,算法,include
From: https://www.cnblogs.com/I-am-Sino/p/16970790.html

相关文章