算法学习笔记(24):筛法
筛法
埃氏筛
考虑埃氏筛是通过把每个质数的倍数都标记为合数, 这样剩下的数就都是质数。
时间复杂度 \(O(nloglogn)\)
bitset<10000000> p;
void Eratosthenes(int n) {
for(int i = 2; i <= n; i++) {
if(!p[i]) prime[++tot] = i;
for(int j = i << 1; j <= n; j += i)
p.set(j, true);
}
}
虽然时间复杂度高于线性, 但是搭配 bitset 效率会比欧拉筛还要优秀!
欧拉筛
欧拉筛时间复杂度 \(O(n)\), 具体就是每个合数只会被其最小质因子筛一次。
bool p[10000000];
void Euler(int n) {
for(int i = 2; i <= n; i++) {
if(!p[i]) prime[++tot] = i;
for(int j = 1; j <= tot && i * prime[j] <= n; j++) {
p[i * prime[j]] = 1;
if(i % prime[j] == 0) break;
}
}
}
看起来很神奇, 两层 for 循环时间复杂度 \(O(n)\)。
正确性证明:
考虑每个合数可以表示成 \(a = b * p1\), \(p1\) 就是 \(a\) 的最小质因子, 考虑 \(b\) 一定会筛到 \(a\), 因为 \(p1\) 一定也是 \(a\) 的最小质因子, 所以在 break 前一定会筛到 \(a\)。
时间复杂度证明:
考虑每个合数可以表示成 \(a = b * p1\), \(p1\) 就是 \(a\) 的最小质因子, 考虑拿 \(b\) 去筛的时候只会筛到 \(a\) 一次, 因为我们枚举的质数是递增的。
现在我们就是证明是否存在一个 \(c\), \(a = c * p2\)。 \(c\) 会筛到 \(a\), 考虑 \(p2\) 一定大于 \(p1\), 因为 \(p1\) 是最小质因子。 并且 \(p1\) 一定是 \(c\) 的因子, 所以在我们枚举质数时枚举到 \(p1\) 时候就会 break 掉, 所以不会筛到 \(a\)。
欧拉函数
首先给出公式:
\(\large \phi(n) = n\prod \frac{p_i - 1}{p_i}\)
考虑用线性筛。
当 \(n \ mod \ p == 0\) 时, \(n\) 包含了 \(n * p\)
\(
\begin{aligned}
\phi(n) & = n \times \prod_{i = 1}^s{\frac{p_i - 1}{p_i}} \\\\
& = p_1 \times n' \times \prod_{i = 1}^s{\frac{p_i - 1}{p_i}} \\\\
& = p_1 \times \varphi(n')
\end{aligned}
\)
当 \(n \ mod \ p != 0\) 时, 则 \(n\) 与 \(p\) 互质
\( \begin{aligned} \phi(n * p) &= \phi(p) * \phi(n) \\\\ &= (p - 1) * \phi(n) \\\\ \end{aligned} \)
代码:
注意 \(\phi[1] = 1\)。
点击查看代码
bool p[10000000];
void Euler(int n) {
phi[1] = 1;
for(int i = 2; i <= n; i++) {
if(!p[i]) prime[++tot] = i, phi[i] = i - 1;
for(int j = 1; j <= tot && i * prime[j] <= n; j++) {
p[i * prime[j]] = 1;
if(i % prime[j] == 0) {
phi[i * prime[j]] = prime[j] * phi[i];
break;
}
else
phi[i * prime[j]] = (prime[j] - 1) * phi[i];
}
}
}
莫比乌斯函数
定义如下:
\(\mu(n)= \begin{cases}
1&n=1\\
0&n\text{ 含有平方因子}\\
(-1)^k&k\text{ 为 }n\text{ 的本质不同质因子个数}\\
\end{cases}\)
在用欧拉筛时则有:
\(
\mu(n)=
\begin{cases}
0 & n' \bmod p_1 = 0\\\\
-\mu(n') & \text{otherwise}
\end{cases}
\)
证明:
当 \(i \ mod \ p == 0\) 时, \(i * p\) 就含有平方因子p, 所以 \(\mu(i * p) = 0\)。
当 \(i \ mod \ p != 0\) 时, \(\mu(i * p)\) 就等于 \(-\mu(i)\), 由定义可得。
代码:
注意 \(\mu[1] = 1\)。
点击查看代码
bool p[10000000];
void Euler(int n) {
mu[1] = 1;
for(int i = 2; i <= n; i++) {
if(!p[i]) prime[++tot] = i;
for(int j = 1; j <= tot && i * prime[j] <= n; j++) {
p[i * prime[j]] = 1;
if(i % prime[j] == 0) {
mu[i * prime[j]] = 0;
break;
}
else
mu[i * prime[j]] = -mu[i];
}
}
}
约数个数函数
\(d(i * p) = 2d(i) - d(\frac{i}{p})\)
约数和函数
$ \sigma(i * p) = p(\sigma(i) - \sigma(\frac{i}{p})) + \sigma(i) $
其他情况就拿通项公式和积性函数算即可。