public static List<Integer> generatePrimes(int max) {
BitSet primeSet = new BitSet(max / 2); // host the numbers i up to max/2
primeSet.set(1, max / 2); // representing the odds (2i+1)
int limit = (int) Math.sqrt(max); // below max
for (int i = 3; i <= limit; i += 2) // enumerate odds in range
{ // 3 .. sqrt(max)
if (!primeSet.get(i / 2)) continue; // i=2k+1, i/2==(2k+1)/2== k
// (here i is value, k is index)
for (int j = i * i; j < max; j += i * 2) // j=i*i is the first multiple
primeSet.clear(j / 2); // of i, where the marking off begins
} // with step 2*i: 3: 9,6,15,21,...
// 7: 49,63,77,91,...
List<Integer> listOfPrimes = new ArrayList<>();
listOfPrimes.add(2); // 2 is known to be prime a priori
for (int i = primeSet.nextSetBit(0); // starting with first set bit in
// BitSet primeSet,
i >= 0; // 1: until the end of primeSet
i = primeSet.nextSetBit(i + 1) // 3: and go to next set bit
) {
listOfPrimes.add(i * 2 + 1); // 2: add 2i+1 to the list of primes,
} // (here i is index)
return listOfPrimes;
}
作为筛子的一部分,我们必须在赔率中从 9 开始标记每个第三个数字,并且通常从n 2开始标记每个第n个数字,正如一位Rev. Samuel Horsley FRS 早在 1772 年就知道的那样。
仅沿列表计数是低效的——筛子效率的关键是通过地址直接访问内存。这里的数字数组中的数字的地址就是数字本身的值(这种值和地址的混淆也是各种整数排序方法效率的关键)。
要直接计算每个第三个奇数,我们必须在前一个奇数上加 6 才能得到下一个奇数。对于每 5 个,我们添加 10,并且对于每个i
th – 2*i
。
顺便说一下,这段代码可以稍微改进。对于2*i
它们之间距离的数字,集合中的索引将在距离i
. 无需一直删除 2,只需计算起始索引并递增i
.
编辑:该代码等效于以下伪代码:
defn primes(max):
sieve := makeArray(3,5,7 ... max, True)
for p from 3 to sqrt(max) step 2:
if sieve[p]:
for i from p * p to max step 2*p:
sieve[i] := False
primes = {2} + {all i in (3,5,7, ... max) such that sieve[i] is True}