数论(二)

素数

设 。如果 除了 和 之外没有别的因数,则称 是素元。正的素元称为素数。

算术基本定理

任何非零整数 都有素因子分解

其中 (当 时右式规定为 ), 是相异素数,,而且此分解不论顺序是唯一的。

证明 关于分解的存在性,处理 的情形即可。我们寻求形如 的分解。如果 既非 又非素数,则分解为 ,其中 。继续对 递归地操作,最终可表 为若干个素数的乘积,容许重复。

唯一性仍可简化到 的情形。设 ,其中 是相异素数, 也是相异素数,而 。注意到 当且仅当 ,此时两边都是 。故以下不妨设 。

由于 ,反复应用欧几里得引理可知存在 使得 ;这进一步蕴含 。重排下标后不妨假设 ,必要时等号两边互换,不妨假设 。于是

再次应用欧几里得引理可见 不整除左式,故 。按此递归地论证,即得分解的唯一性。

算术基本定理的推论

对于任何素数 和非零整数 ,我们有 当且仅当 在 的素因子分解中出现,相应的指数 由以下性质唯一确定: 而 ,数论中的标准记法如下

  • 设 为素数,我们以符号 表达 而 。

考虑正整数 和 ,其中 是相异素数而 ,则

  • 对于任意多个正整数的 和 也有类似结果。

定义 为正整数 的正因数的个数。例如 ,,。

设正整数 的素因子分解是 ,那么 。

例题:Product of Divisors

求 的全部正因数的乘积能被 整除的次数,模 。

的正因数的乘积等于 。

若 是 的因数且 ,那么 也是 的因数。

有无穷多个素数

素数列 是数论关切的基本对象;这方面最古老也是最基础的结果是

存在无穷多个素数。

证明 对任意一列素数 ,考虑

则 ,而且它不被 中任一个素数整除。因此 的素因子分解中必有不同于 的素数。

给定正整数 ,找出不超过 的全部素数。

素数筛

我们试图制造一个筛子。

我们把整数 放进筛子,启动筛子。筛子会把其中的合数全都筛出去,素数全都保留。

筛完之后,筛子里剩下的就是不超过 的全部素数。

埃氏筛

vector<int> get_primes(int n) {
    vector<int> primes;
    vector<bool> is_prime(n + 1, true);
    for (int i = 2; i <= n; i++)
        if (is_prime[i]) {
            primes.push_back(i);
            for (int j = 2 * i; j <= n; j += i)
                is_prime[j] = false;
        }
    return primes;
}

时间复杂度:。

埃氏筛的时间复杂度

线性筛

Lucy_Hedgehog 算法

给定正整数 ,求不超过 的素数有多少个。。

  • 我们以 表示第 个素数。例如 ,,。
  • 设 是实数,以 表示不超过 的素数的个数。
    例如:,若 ;;;。
    我们要计算 。

计算

对于实数 和正整数 ,定义 为从区间 中去掉最小素因子 的合数之后,剩下的整数的数量。此外,定义 。

设 是不超过 的最大的素数,即 ,那么 。

设 是正整数。有以下递推式

证明:根据定义,我们有

区间上最小素因子是的合数的数量区间上最小素因子的整数的数量

当 ,即 时,区间 上没有最小素因子 的整数,此时有

当 ,即 时,区间 上最小素因子 的整数的个数是

显然有 。

对于任意正整数 有 。

设 用带余除法表作 ,其中 。再设 用带余除法表作 ,其中 。于是 。

将 代入 ,得

显然有 ,我们只需要证明 。我们有

计算

给定正整数 。令 。根据递推式

计算 。

我们需要计算的子问题形如 ,其中 满足

  • ,
  • 是 中一些相异的素数的乘积。因而有 或 。

设 是正整数,定义集合

例如

  • ,
  • ,
  • 。

设 是正整数。令 。那么集合 里有 个元素,从大到小排列是

其中 是连续的。

在 里,我们把 称为大值,把 称为小值。

设 是正整数,,那么 。

由 可知 ,因此 。下面证明等号不能成立,从而得到严格大于。

假设 ,即 且 ,即

于是我们有 ,即 ,又 ,所以

另一方面,根据题设,有 ,所以 ;结合 式的右边 ,得 ,所以 ,即

所以 不存在。

设 是正整数,。那么

也就是说,对于 里的每个大值,若把它表作 ,那么 是唯一的。

设 是正整数,。那么 。

令 ,我们证明 。

根据上一个命题,有 ,即 ,所以

又 ,所以

即

即 .

.

令 。 里的元素从小到大排列是

其中, 可能等于 。

long long prime_count(long long n) {
    const int k = (int) sqrt(n);
    vector<long long> v(k + 1);
    for (int i = 1; i <= k; i++)
        v[i] = n / i;
    for (int i = n / (k + 1); i >= 1; i--)
        v.push_back(i);
    // 对于 i = 1, 2, ..., k+1 有 n/i == v[i-1]。
    // 对于 i = 1, 2, ..., k 还有,i 是唯一的满足 n/d == n/i 的正整数 d。我们称这些值为大值。
    const int m = (int) v.size();
    vector<long long> f(m);
    for (int i = 1; i < m; i++)
        f[i] = v[i] - 1;
    
    auto [p, lpf] = get_primes(k);
    const int s = (int) p.size();
    for (int i = 0; i < s; i++) {
        f[1] -= f[p[i]] - i;
        if (i == s - 1) break;
        const long long L = (long long) p[i] * p[i];
        const int lim = k / p[i];
        for (int j = p[i + 1]; j < m && v[j] >= L; j++) {
            if (j < k && lpf[j + 1] < p[i + 1]) continue;
            if (j <= lim) {
                f[j] -= f[j * p[i]] - i;
            } else { // v[j] / p[i] <= k
                f[j] -= f[m - v[j] / p[i]] - i;
            }
        }
    }
    return f[1];
}

时间:。

设 是正整数。对于任意正整数 ,定义 为 中大于等于 的元素的个数,那么

设 是正整数。若 则 。

设 是正整数,。那么 。

题设 ,即 ,即

又因为 ,所以 。于是有 ,由于 ,有

把 和 结合,得 ,即

即 ,命题得证。

积性函数的前缀和

我感觉这其实就是在应用容斥原理。这里值得深挖。