愛拉托散尼篩法
愛拉托散尼篩法(古希臘語:κόσκινον Ἐρατοσθένους,英語:sieve of Eratosthenes),簡稱愛氏篩,是一種用來生成質數的篩法,得名於古希臘數學家愛拉托散尼。其基本步驟是從最小的質數2開始,將該質數的所有倍數標記成合數,而下一個尚未被標記的最小自然數3即是下一個質數。如此重複這一過程,將各個質數的倍數標記為合數並找出下一個質數,最終便可找出一定範圍內所有質數。
愛拉托散尼篩法可能在愛拉托散尼的時代之前就已經為人所知[1]:14,並記載於另一位古希臘數學家尼科馬庫斯的《算術概論》中,儘管該著作中的這一篩法是從3開始,從奇數中依次篩去奇數的倍數,而非從自然數中篩去質數的倍數[2]:242-243。
運用與示例
愛拉托散尼篩法通過不斷地標記當前質數的所有倍數為合數,從而取得最小的未標記整數為下一個質數。不過,在實際使用此篩法尋找一個範圍內的質數時,不需要檢查範圍內所有整數,也不需要對每個質數都標記其所有的倍數。
- 尋找 以內的質數時,若找到了一個大於 的質數,則剩餘的所有尚未標記的數也都是質數。
- 標記某一質數 的倍數時,不需要每次皆從 開始,而可直接從 開始標記。
- 證明:所有較 更小的 的倍數必然擁有一個更小的質數為其因數,故在標記之前的質數的倍數時它們已經被標記過了[5]。
若要找出25以內的所有質數,使用如上述改進過的愛拉托散尼篩法的具體過程如下:
- 列出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
- 記錄質數2,由22=4開始划去2的倍數:
- 2 3
45678910111213141516171819202122232425
- 2 3
- 記錄下一質數3,由32=9開始划去3的倍數:
- 2 3
45678910111213141516171819202122232425
- 2 3
- 記錄下一質數5,由52=25開始划去5的倍數:
- 2 3
45678910111213141516171819202122232425
- 2 3
- 下一質數為7,而72=49>25,故剩餘所有未標記的數皆為質數:
- 2 3
45678910111213141516171819202122232425
- 2 3
由此得到25內的質數為2,3,5,7,11,13,17,19,23。
以上的算法可用以下虛擬碼表示:
輸入:整數n > 1
設A為布爾值陣列,指標是2至n的整數,
初始時全部設成true。
for i = 2, 3, 4, ..., 不超過 :
if A[i]為true:
for j = i2, i2+i, i2+2i, i2+3i, ..., 不超過n:
A[j] := false
輸出:使A[i]為true的所有i。
愛拉托散尼篩法的時間複雜度為 ;相比之下,若是通過對範圍內每個整數進行試除法來找出範圍內的質數,則其時間複雜度為 [1]:13-14[5]。
程式碼
Python 3.6-3.10
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語言
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語言新版
#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++
#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
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
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;
};
參見
參考文獻
- ^ 1.0 1.1 Stefan Hougardy; Jens Vygen. Algorithmic Mathematics. Cham: Springer International Publishing Switzerland. 2016 [2024-01-05]. ISBN 978-3-319-39558-6. (原始內容存檔於2024-01-05).
- ^ Jean-Luc Chabert. A History of Algorithms: From the Pebble to the Microchip. Berlin, Heidelberg: Springer-Verlag. 1999 [2024-01-05]. ISBN 978-3-642-18192-4. (原始內容存檔於2024-01-05).
- ^ George M. Phillips. Mathematics Is Not a Spectator Sport. New York: Springer-Verlag. 2005 [2024-01-05]. ISBN 978-0-387-28697-6. (原始內容存檔於2024-01-05).
- ^ G. H. Hardy; E. M. Wright. An Introduction to the Theory of Numbers (Fourth Edition). Oxford: Clarendon Press. 1960. ISBN 978-0-19-853310-8.
- ^ 5.0 5.1 Melissa E. O'Neill. The Genuine Sieve of Eratosthenes (PDF). Journal of Functional Programming. 2009, 19 (1): 95–106 [2024-01-05]. doi:10.1017/S0956796808007004. (原始內容存檔 (PDF)於2024-07-26).