函数接受一个整数
n
作为参数,并返回一个小于等于n
的所有素数的数组。
import java.util.Arrays;
public static int[] sieveOfEratosthenes(int n) {
boolean[] isPrime = new boolean[n + 1];
for (int i = 2; i <= n; i++) {
isPrime[i] = true;
}
int[] primes = new int[n];
int count = 0;
for (int i = 2; i <= n; i++) {
if (isPrime[i]) {
primes[count++] = i;
}
for (int j = 0; j < count && i * primes[j] <= n; j++) {
isPrime[i * primes[j]] = false;
if (i % primes[j] == 0) {
break;
}
}
}
return Arrays.copyOf(primes, count);
}
初始化isPrime数组
boolean[] isPrime = new boolean[n + 1];
for (int i = 2; i <= n; i++) {
isPrime[i] = true;
}
我们将所有数初始化为素数(isPrime[i] = true
),因为一开始我们假设所有数都是素数,然后通过后续的筛选过程将非素数标记为false
。
初始化primes数组
int[] primes = new int[n];
int count = 0;
创建一个数组primes,用于存储找到的素数,(数组的大小为n,但实际上我们可能并不需要存储所有的n个数,因为素数在总数中只占一小部分,但为了简化代码,我们先这样初始化 )
count用于记录到目前为止找到的素数个数
双循环部分
for (int i = 2; i <= n; i++) {
if (isPrime[i]) {
primes[count++] = i;
}
for (int j = 0; j < count && i * primes[j] <= n; j++) {
isPrime[i * primes[j]] = false;
if (i % primes[j] == 0) {
break;
}
}
}
外循环 for (int i = 2; i <= n; i++)
遍历从2到n的所有整数。对于每个整数i,我们检查它是否是素数,并用它来筛去合数。
内循环 for (int j = 0; j < count && i * primes[j] <= n; j++)
是一个嵌套的循环,用于筛去合数。这个循环使用已经找到的素数(存储在primes
数组中)来标记合数。
- 外循环的作用:
- 对于每个整数i,首先检查
isPrime[i]
是否为true
。如果是,说明i是一个素数,我们将其添加到primes
数组中,并增加count
的值。 - 接下来,内循环开始执行,使用i和已经找到的素数来筛去合数。
- 对于每个整数i,首先检查
- 内循环的作用:
primes[j]
是已经找到的素数,我们用它来检查i * primes[j]
是否是合数。如果是,我们将isPrime[i * primes[j]]
设为false
,标记它为一个非素数。- 内循环会继续执行,直到满足以下任一条件:
j
达到count
的值,即已经用所有已知的素数筛过i了。i * primes[j]
大于n,即不再需要筛更大的数了。
- 内循环中有一个重要的优化:如果
i
能被primes[j]
整除(即i % primes[j] == 0
),那么i
一定不是素数(因为除了1和它本身以外,它还有其他的因数)。在这种情况下,i
与primes[j+1]
、primes[j+2]
等的乘积都已经在之前的循环中被primes[j]
筛过了,因此无需再次筛除。通过break
语句跳出内循环,可以节省不必要的计算。
通过这个双循环,可以有效地找到小于等于n的所有素数,并将它们存储在primes
数组中。最后,使用Arrays.copyOf
方法返回包含实际素数个数的数组。
这个算法的关键在于利用已经找到的素数来高效地筛去合数,避免了不必要的重复计算,从而提高了算法的效率。