质数筛
Table of Contents
两种筛的分析
感谢 @LS-Hower 的勘误
埃拉托斯特尼筛
对于每一个质数 $p$,标记其倍数 $p^2,p(p+1),p(p+2)\cdots$,对于足够大的 $n$ 和固定的 $p$,标记次数约为
$$T(n)\sim \sum_{p\le n} \frac{n}{p} = n\sum _{p\le n}\frac{1}{p}$$利用质数调和级数的渐近式
$$\sum _{p\le n} \frac{1}{p} = \log\log n+B+o(1)$$从而 $T(n)=n\log\log(n)$。有一个 $\log\log n$ 的因子,说明重复访问。
欧拉筛
记 $\text{lp}(n)$ 为 $n$ 的最小质因子(least prime factor)。由最小质因数引入划分 $\mathcal{L}_p=\{n\ge 2;\text{lp}(n)=p\}$。
由二元对 $(n,p)$ 双射地标记一个合数 $x=n\times p$,满足性质 $\text{lp}(x)=p$。
为了给出遍历算法,我们让 $n$ 自然生长,而限制 $p\in \mathcal{P}$ 的范围 $p\le \text{lp}(n)$,以保持 $\text{lp}(x)=p$ 的性质。每个合数仅被其最小质因子筛去一次,因此算法时间复杂度为 $O(n)$。
- $p\le \text{lp}(n)$ 的证明
- 设 $p_n:=\text{lp}(n)$,那么 $\text{lp}(n\times p)=\min\{p,p_n\} = p$,说明当 $p \leq \text{lp}(n)$ 时,性质得到延续。
- 若 $p\gt p_n$,$\text{lp}(n\times p)=\min\{p,p_n\} = p_n\neq p$,性质被破坏。此时得到的合数 $n\times p$ 应该隶属于 $\mathcal{L}_{p_n}$ 而非 $\mathcal{L}_p$,因此不应继续用更大的质数去枚举这个 $n$ 的后继标记。
积性函数的自然分解
基本概念
$f:\mathbb{N}\to \mathbb{C}$ 称积性的,如果对于互质的 $m,n$,$f(mn) = f(m)f(n)$。
如果对所有 $m,n$ 都满足,则称为完全积性的。
定义狄利克雷卷积
$$ (f*g)(n):= \sum_{d \mid n}f(d)g \Big(\frac{n}{d} \Big ) $$狄利克雷卷积可以保持积性。
定义 $\varepsilon := \llbracket n=1\rrbracket, 1(n) = 1, \text{id}(n)=n$,那么 $1*\mu=\varepsilon, 1*1=\tau, \text{id}*1=\sigma, 1*\varphi=\text{id}$
一点代码
fn linear_sieve(n: usize) -> (Vec<usize>, Vec<usize>) {
let mut lp = vec![0usize; n + 1]; // least prime factor
let mut primes = Vec::new();
for i in 2..=n {
if lp[i] == 0 {
lp[i] = i;
primes.push(i);
}
for &p in &primes {
let x = i * p;
if x > n {
break;
}
lp[x] = p;
if p == lp[i] {
break;
}
}
}
(lp, primes)
}
import Control.Monad
import Control.Monad.ST
import Data.Array.ST
import Data.Array.Unboxed
linearSieve :: Int -> (UArray Int Int, [Int])
linearSieve n = runST $ do
lp <- newArray (0, n) 0 :: ST s (STUArray s Int Int)
primesRef <- newSTRef []
forM_ [2..n] $ \i -> do
v <- readArray lp i
when (v == 0) $ do
writeArray lp i i
modifySTRef primesRef (i:)
primes <- readSTRef primesRef
lpi <- readArray lp i
forM_ primes $ \p -> do
when (p <= lpi && i * p <= n) $ do
writeArray lp (i*p) p
lp' <- freeze lp
primes <- fmap reverse (readSTRef primesRef)
return (lp', primes)