原理

令當前要處理的數為 $i$,已找到的質數列表為 primes

  1. 遍歷 $i$ 從 $2$ 到 $N$。
  2. 若 $i$ 未被標記為合數,則 $i$ 為質數,加入 primes
  3. 用 $i$ 與 primes 中的質數 $p$ 相乘(標記合數 $x = i \times p$):
    • 標記 $i \times p$ 為合數。
    • 關鍵關鍵條件:若 $i \pmod p == 0$,立即中斷(break)內部迴圈

為什麼 i % p == 0 時要 break?
線性篩規定「每個合數只能由它的最小質因數篩掉」,若 $p$ 能整除 $i$,表示 $p$ 是 $i$ 的最小質因數。
如果繼續使用下一個更大的質數 $p_{next}$ 來標記 $i \times p_{next}$,該數的最小質因數其實是 $p$ 而非 $p_{next}$。這樣會導致 $i \times p_{next}$ 未來又被別的更大的 $i$ 和 $p$ 重複篩到,破壞線性時間。

可以想像 $i = a \times p$,有一數 $b = i \times p', p' > p$ 是我們沒有 break 繼續執行篩掉的數,將來等 $i$ 跑到 $i' = a \times p'$,又會遍歷一次 $b = i' \times p = a \times p \times p'$ ,造成多餘的執行結果,break 就是要確保每個數字都會被 $i \times p$ 中最小的 $p$ 篩掉。

舉例來說,$12 = 2 \times 6 = 4 \times 3$ 。如果不執行 break,那麼 $12$ 在 $i = 4$ 的時候會被篩過一次,在 $i = 6$ 時又被篩過一次。如果現在 $i = 4$ 且我們發現 $4 \pmod 2 = 0$ ,表示 $4$ 不是 $12$ 的最小質因數,所以要停下,否則 $12$ 會被篩兩次。

埃氏篩之所以會有 $O(n\log \log n)$ 的時間複雜度,就是因為它會重複篩掉同一個數。

實作

const int MAXN = 1000000;
bool is_composite[MAXN + 1]; // is_composite[x] 為 true 表示 x 是合數
vector<int> primes;

void linear_sieve(int n) {
    for (int i = 2; i <= n; ++i) {
        if (!is_composite[i]) {
            primes.push_back(i);
        }
        for (int p : primes) {
            if (i * p > n) break; // 超出範圍則跳出
            
            is_composite[i * p] = true; // 用最小質因數 p 標記合數
            
            if (i % p == 0) {
                break; // 保證每個合數只被最小質因數篩除一次
            }
        }
    }
}

歐拉函數 $\phi (n)$

定義

$\phi(n)$ 表示在 $1 \le k \le n$ 中,與 $n$ 互質的整數個數。

  • 質數 $p$ 的歐拉函數:$\phi(p) = p - 1$。
  • 通用公式(基於質因數分解 $n = p_1^{e_1} p_2^{e_2} \dots p_k^{e_k}$):
    $$\phi(n) = n \left(1 - \frac{1}{p_1}\right)\left(1 - \frac{1}{p_2}\right)\dots\left(1 - \frac{1}{p_k}\right)$$

性質

當我們用最小質因數 $p$ 去乘上 $i$ 得到新數 $n = i \times p$ 時:

  1. 若 $i \pmod p \ne 0$($p$ 不是 $i$ 的因數):
    $i$ 與 $p$ 互質,利用積性函數性質: $$\phi(i \times p) = \phi(i) \times \phi(p) = \phi(i) \times (p - 1)$$
  2. 若 $i \pmod p == 0$($p$ 已經是 $i$ 的因數):
    所以 $i \times p$ 與 $i$ 擁有完全一樣的質因數組合,根據公式:
    $$\phi(i \times p) = \mathbf{(i \times p)} \times \left(1 - \frac{1}{p}\right) \left(1 - \frac{1}{q_1}\right) \left(1 - \frac{1}{q_2}\right)$$
    我們把 $\phi(i \times p)$ 的式子重新整理一下:
    $$\phi(i \times p) = p \times \underbrace{\left[ \mathbf{i} \times \left(1 - \frac{1}{p}\right) \left(1 - \frac{1}{q_1}\right) \left(1 - \frac{1}{q_2}\right) \right]}_{\text{這整串就是 } \phi(i)}$$
    所以:
    $$\phi(i \times p) = p \times \phi(i)$$

莫比烏斯函數 $\mu(n)$

定義

$\mu(n)$ 是莫比烏斯反演(Möbius Inversion)的核心函數,定義如下:

  • $\mu(1) = 1$
  • 若 $n$ 包含任何次方大於 $1$ 的質因數,則 $\mu(n) = 0$。
  • 若 $n$ 為 $k$ 個互不相同的質數之積($n = p_1 p_2 \dots p_k$),則 $\mu(n) = (-1)^k$。

性質

當我們用最小質因數 $p$ 去乘上 $i$ 得到新數 $n = i \times p$ 時:

  1. 若 $i \pmod p \ne 0$
    $i \times p$ 比 $i$ 多了一個不同的質因數,質因數個數加 $1$: $$\mu(i \times p) = -\mu(i)$$
  2. 若 $i \pmod p == 0$
    $i \times p$ 包含了至少兩個 $p$(即含有 $p^2$ 平方因子): $$\mu(i \times p) = 0$$

實作

const int MAXN = 1000000;
bool is_composite[MAXN + 1];
vector<int> primes;

int lpf[MAXN + 1]; // 紀錄最小質因數
int phi[MAXN + 1]; // 歐拉函數
int mu[MAXN + 1]; // 莫比烏斯函數

void sieve(int n) {
    lpf[1] = 1;
    phi[1] = 1;
    mu[1] = 1;

    for (int i = 2; i <= n; ++i) {
        if (!is_composite[i]) {
            primes.push_back(i);
            lpf[i] = i;
            phi[i] = i - 1; // 質數 p 的 phi(p) = p - 1
            mu[i] = -1;    // 質數 p 的 mu(p) = -1
        }
        
        for (int p : primes) {
            if (i * p > n) break;
            
            is_composite[i * p] = true;
            lpf[i * p] = p;
            
            if (i % p == 0) {
                // p 是 i 的因數
                phi[i * p] = phi[i] * p;
                mu[i * p] = 0; // 包含平方因子 p^2
                break;         // 保證線性複雜度
            } else {
                // p 與 i 互質
                phi[i * p] = phi[i] * (p - 1);
                mu[i * p] = -mu[i];
            }
        }
    }
}