埃拉托色尼篩法

埃拉托色尼篩法（κόσκινον Ἐρατοσθένους，sieve of Eratosthenes），或作埃拉托斯特尼篩法，簡稱埃氏篩，是一種用來質數生成質數的篩法，得名於古希臘數學家埃拉托色尼。其基本步驟是從最小的質數2開始，將該質數的所有倍數標記成合數，而下一個尚未被標記的最小自然數3即是下一個質數。如此重複這一過程，將各個質數的倍數標記為合數並找出下一個質數，最終便可找出一定範圍內所有質數。

埃拉托色尼篩法可能在埃拉托色尼的時代之前就已經為人所知，並記載於另一位古希臘數學家尼科馬庫斯的《算術概論》中，儘管該著作中的這一篩法是從3開始，從奇數中依次篩去奇數的倍數，而非從自然數中篩去質數的倍數。

使用埃拉托色尼篩法找出120以內的所有質數。由於112=121>120，當11成為最小的未標記整數時，尚未標記的所有數皆可確認為質數。請注意到在標記時直接從每個質數的平方開始。

## 運用與示例

埃拉托色尼篩法通過不斷地標記當前質數的所有倍數為合數，從而取得最小的未標記整數為下一個質數。不過，在實際使用此篩法尋找一個範圍內的質數時，不需要檢查範圍內所有整數，也不需要對每個質數都標記其所有的倍數。

- 尋找$N$以內的質數時，若找到了一個大於$\sqrt{N}$的質數，則剩餘的所有尚未標記的數也都是質數。
- 證明：若這些尚未標記的數中有任意一個為合數，設之為$m$，則$m$必定是除1與自身以外的兩個因數的乘積。但既然$m$尚未被標記，則所有小於等於$\sqrt{N}$的數均不是$m$的因數。故這兩個因數必然都大於$\sqrt{N}$，則$m$不可能在$N$以內。
- 標記某一質數$p$的倍數時，不需要每次皆從$2 p, 3 p, \ldots$開始，而可直接從$p^2$開始標記。
- 證明：所有較$p^2$更小的$p$的倍數必然擁有一個更小的質數為其因數，故在標記之前的質數的倍數時它們已經被標記過了。

若要找出25以內的所有質數，使用如上述改進過的埃拉托色尼篩法的具體過程如下：

- 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
- 記錄質數2，由22=4開始划去2的倍數：
- 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
- 記錄下一質數3，由32=9開始划去3的倍數：
- 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
- 記錄下一質數5，由52=25開始划去5的倍數：
- 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
- 下一質數為7，而72=49>25，故剩餘所有未標記的數皆為質數：
- 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25

由此得到25內的質數為2，3，5，7，11，13，17，19，23。

以上的演算法可用以下虛擬碼表示：

輸入：整數n > 1

```
&nbsp
```

埃拉托色尼篩法的時間複雜度為$O(n\log (\log n))$；相比之下，若是通過對範圍內每個整數進行試除法來找出範圍內的質數，則其時間複雜度為$O(n\sqrt {n})$。

## 代碼

### Python 3.6-3.10

```python
def eratosthenes(n):
    is_prime = [True] * (n + 1)
    for i in range(2, int(n ** 0.5) + 1):
        if is_prime[i]:
            for j in range(i * i, n + 1, i):
                is_prime[j] = False
    return [x for x in range(2, n + 1) if is_prime[x]]
print(eratosthenes(120))
```

### C語言

```c
int prime[100005];
bool is_prime[1000005];

int eratosthenes(int n) {
    int p = 0;
    for (int i = 0; i <= n; i++) {
        is_prime[i] = true;
    }
    is_prime[0] = is_prime[1] = 0;
    for (int i = 2; i <= n; i++) {
        if (is_prime[i]) {
            prime[p++] = i;
            if (1ll * i * i <= n) {
                for (int j = i * i; j <= n; j += i) {
                    is_prime[j] = 0;
                }
            }
        }
    }
    return p;
}
```

C語言新版

```c
#include <stdio.h>
#include <stdlib.h>

/* N: positive integer
   verbose: 1 -- print all prime numbers < N, 0 -- no print
   return total number of prime numbers < N. 
   return -1 when there is not enough memory.
*/
int eratosthenesSieve(unsigned long long int N, int verbose) {
  // prime numbers are positive, better to use largest unsiged integer
  unsigned long long int i, j, total; // total: number of prime numbers < N
  _Bool *a = malloc(sizeof(_Bool) * N);

  if (a == NULL) {
    printf("No enough memory.\n");
    return -1;
  }
  
  /* a[i] equals 1: i is prime number.
     a[i] equals 0: i is not prime number.
     From beginning, set i as prime number. Later filter out non-prime numbers
  */
  for (i = 2; i < N; i++) {
    a[i] = 1; 
  }

  // mark multiples(<N) of i as non-prime numbers
  for (i = 2; i < N; i++) {
    if (a[i]) { // a[i] is prime number at this point
      for (j = i; j < (N / i) + 1; j++) {
	/* mark all multiple of 2 * 2, 2 * 3, as non-prime numbers;
	   do the same for 3,4,5,... 2*3 is filter out when i is 2
	   so when i is 3, we only start at 3 * 3
	*/
	a[i * j] = 0;
      }
    }
  }

  // count total. print prime numbers < N if needed.
  total = 0;
  for (i = 2; i < N; i++) {
    if (a[i]) { // i is prime number
      if (verbose) {
	printf("%llu\n", i);
      }
      total += 1;
    }
  }

  return total;
}

int main() {
  unsigned long long int a1 = 0, a2 = 0, N = 10000000;
  
  a1 = eratosthenesSieve(N, 1); // print the prime numbers
  printf("Total of prime numbers less than %llu is : %llu\n", N, a1);
  
  a2 = eratosthenesSieve(N, 0); // not print the prime numbers
  printf("Total of prime numbers less than %llu is : %llu\n", N, a2);
  
  return 0;
}
```

### C++

```c++
#include <vector>

auto eratosthenes(int upperbound) {
  std::vector<bool> flag(upperbound + 1, true);
  flag[0] = flag[1] = false; //exclude 0 and 1
  for (int i = 2; i * i <= upperbound; ++i) {
    if (flag[i]) {
      for (int j = i * i; j <= upperbound; j += i)
        flag[j] = false;
    }
  }
  return flag;
}
```

### R

```r
eratosthenes <- function(n) {
  if (n == 1) return(NULL)
  if (n == 2 | n == 3) return(2:n)
  numbers <- 2:n
  primes <- rep(TRUE, n-1)
  for (i in 2:floor(sqrt(n))) {
    if (primes[i-1]) {
      for (j in seq(i * i, n, i))
        primes[j-1] <- FALSE
    }
  }
  return(numbers[primes])
}
```

### JavaScript

```js
const countPrimes = function (n) {
  const isPrime = new Array(n).fill(true);

  for (let i = 2; i <= Math.sqrt(n); i++) {
    if (isPrime[i]) {
      for (let j = i * i; j <= n; j += i) {
        isPrime[j] = false;
      }
    }
  }

  let count = 0;
  for (let i = 2; i < n; i++) {
    if (isPrime[i]) {
      count++;
    }
  }

  return count;
};
```

## 拓展閱讀

- Interactive animation (需要JavaScript)
- 列印質數的各種演算法
- 篩法小結 (Eratosthenes/Euler)
- 歐拉函數線性篩法詳解 （歐拉函數線性篩）
- 一般篩法求素數+快速線性篩法求素數(較好理解)