【数论】——质数「与质数相关的内容都在这里了,快收藏吧!」

490 阅读3分钟

质数

质数基本性质

概念

  • 对于一正整数 a,如果 a 的因数有且仅有 1a,则称 a 为质数(素数),否则称 a 为合数。

质数定理

  • 在整个自然数集合中,质数分布相对稀疏,对于一个足够大的数 N[1,N] 中所有的质数个数为:
CountOfPrimes=NlnNCountOfPrimes = \frac{N}{\ln N}

质数的判定

试除法

  • 模版题AcWing 866. 试除法判定质数 枚举 [2,x] 所有整数,如果 x%i == 0x 不是质数。

  • 优化 容易发现,合数 x 的因数总是成对出现,并且关于 x\sqrt{x} 对称,因此只将枚举区间缩小至 [2,x\sqrt{x}] 即可,每次判断 x 是否为质数时间复杂度为严格 O(x\sqrt{x})

  • 代码

bool is_prime(int x)
{
    if (x <= 1)
        return false;
    /*
         这里建议写 i<=x/i
            原因:
            1)写 sqrt(x),要调用 <cmath> 的函数,太慢、
            2)写 i*i<=x, 如果 i 过大会溢出。
    */
    for (int i = 2; i <= x / i; i++) {if (x % i == 0)
            return false;
    }
    return true;
}

质数的筛选

埃氏筛法

  • 对于任意 x,其倍数 2x,3x... 必然不是质数, 从 2 开始扫描所有小于 N 的数,如果当前数为质数,筛除其小于等于 N 的所有倍数。直到筛去 [1,N] 中的所有质数的倍数,就可以筛去区间内所有合数。
  • 优化: 由于对于数字 6,会被 2,3 各筛一遍,因此若当前数 x 为质数,小于 x^2 的所有合数已经之前的质数筛去了,只需要筛除 [x2x^2, [Nx\frac{N}{x}] ] 的所有 x 的倍数即可。
  • 筛除图解——(来自 李煜东 《算法竞赛进阶指南》) iShot2022-02-04 05.01.38.png
  • 复杂度分析 每次将 x 的倍数筛除,需要计算:
cnt=N2+N3+...+NN1+NNcnt=\frac{N}{2}+\frac{N}{3}+...+\frac{N}{N-1}+\frac{N}{N}
cnt=N(12+13+...+1N1+1N)cnt = N\left(\frac{1}{2}+\frac{1}{3}+...+\frac{1}{N-1}+\frac{1}{N} \right)

此式为 调和级数:

cnt=lnN+Ccnt = \ln N+{C}

因此复杂度为:O(nlog(lnn)n\cdot \log \left( \ln n \right))

  • 代码
bool st[N]; // 表示是(true)否被筛
int primes[N], cnt = 0; //primes 存储所有质数,cnt 表示当前质数个数
void Eratosthenes(int n)
{for (int i = 2; i <= n; i++) {if (st[i])
            continue;
        primes[++cnt] = i; // 如果没被筛过,就是质数
        for (int j = i; j <= n / i; j++) // 从 x^2 开始筛去 x 的倍数
            st[j * i] = true;
    }
}

线性筛法

  • 上述从 x2x^2 开始还是会重复筛除,eg:12 ,被 2,3,6 重复标记,因此需要对产生 12 的方式进行固定,只让其被筛除一次以达到线性复杂度 (O(n))
  • 线性筛法,是将一个数 x,只使 x 被其的最小质因数筛除,eg:12 仅被 2 筛除。
  • 实现方式: 在埃式筛法的基础上,每次扫描不大于 ni\frac{n}{i} 的所有质数,将 st[primes[j] * i ] = true 筛除。
  • 筛除图解——(来自 李煜东 《算法竞赛进阶指南》)!

iShot2022-02-04 05.28.12.png

  • 代码
bool st[N]; // 表示是(true)否被筛
int primes[N], cnt = 0; //primes 存储所有质数,cnt 表示当前质数个数
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++){if (i % primes[j] == 0) // 如果可以整除,则存在比当前更小的 prime 可以筛去 i,退出
                break;
            st[primes[j] * i] = true; //primes[j] 是 primes[j] * i 的最小质因子
        }
    }
}