Eratosthenes筛选的Java实现可以超过n = 2 ^ 32?

时间:2015-09-21 04:30:32

标签: java algorithm primes sieve-of-eratosthenes

目前我有这个限制为n< 2 ^ 32-1。鉴于数组中元素的限制,我不完全确定如何进一步扩展限制。

Sieve:

public class Main {

public static void main(String args[]){
    long N = 2000000000;

    // initially assume all integers are prime

    boolean[] isPrime = new boolean[N + 1];
    for (int i = 2; i <= N; i++) {
        isPrime[i] = true;
    }

    // mark non-primes <= N using Sieve of Eratosthenes
    for (int i = 2; i*i <= N; i++) {

        // if i is prime, then mark multiples of i as nonprime
        // suffices to consider mutiples i, i+1, ..., N/i
        if (isPrime[i]) {
            for (int j = i; i*j <= N; j++) {
                isPrime[i*j] = false;
            }
        }
    }
}
}

我如何修改它以超过n = 2 ^ 32-1?

3 个答案:

答案 0 :(得分:4)

您可以使用BitSet个对象数组来表示长位集。这是完整的例子:

public class Main {
    private static class LongBitSet {
        // max value stored in single BitSet
        private static final int BITSET_SIZE = 1 << 30;

        BitSet[] bitsets;

        public LongBitSet(long limit) {
            bitsets = new BitSet[(int) (limit/BITSET_SIZE+1)];
            // set all bits by default
            for(int i=0; i<bitsets.length; i++) {
                bitsets[i] = new BitSet();
                int max = (int) (i == bitsets.length-1 ?
                          limit % BITSET_SIZE : BITSET_SIZE);
                bitsets[i].set(0, max);
            }
        }

        // clear specified bit
        public void clear(long pos) {
            bitsets[(int) (pos / BITSET_SIZE)].clear((int) (pos % BITSET_SIZE));
        }

        // get the value of the specified bit
        public boolean get(long pos) {
            return bitsets[(int) (pos / BITSET_SIZE)].get((int) (pos % BITSET_SIZE));
        }

        // get the number of set bits
        public long cardinality() {
            long cardinality = 0;
            for(BitSet bs : bitsets) {
                cardinality += bs.cardinality();
            }
            return cardinality;
        }
    }

    public static void main(String args[]) {
        long N = 4000000000L;

        // initially assume all integers are prime

        LongBitSet bs = new LongBitSet(N+1);
        // clear 0 and 1: non-primes
        bs.clear(0);
        bs.clear(1);

        // mark non-primes <= N using Sieve of Eratosthenes
        for (long i = 2; i * i <= N; i++) {
            if (bs.get(i)) {
                for (long j = i; i * j <= N; j++) {
                    bs.clear(i * j);
                }
            }
        }
        System.out.println(bs.cardinality());
    }
}

N = 4_000_000_000L的这个程序需要大约512Mb的内存,工作几分钟并打印189961812,这是正确的素数低于4亿according to Wolfram Alpha。如果你有足够的内存,你可以尝试设置更大的N.

答案 1 :(得分:2)

您可以对筛子进行分段:您可以分配许多小型数组,而不是分配单个巨大的数组。如果要查找高达10 ^ 10的素数,可以使用大小为10 ^ 6左右的数组(或更好,BitSet s)。然后你运行筛子10 ^ 4次。每次你运行一个新的细分时,你需要找到从筛子开始每个素数的位置,但这并不是太难。

除了允许更小的内存使用外,这还可以在缓存中保留更多的内存,因此它通常要快得多。

答案 2 :(得分:1)

我看到了选项:

  1. 打包16个数字/ 1个字节

    • 每个位只记住奇数
    • 使用无符号变量来避免符号位浪费
  2. 使用多个表格

    • 但在32位应用程序中,您受操作系统功能的限制
    • 通常为1/2/4 GB的可用内存
    • 这样的大桌子通常不适合CACHE,因此它不是很快
  3. 您可以一次使用更多方法

    • 我将定期筛子与找到的主要列表二进制搜索结合起来
    • 如果您选择正确的尺寸,您甚至可以提高性能
    • 通过更好地使用平台缓存属性
    • 请参阅Prime numbers by Eratosthenes quicker sequential than concurrently?
    • 想法是使用筛子来测试小除数
    • 然后才检查主要列表中的存在
    • 它不需要那么多记忆
    • 并且非常快
  4. 备用内存可以组合16/32/64位变量

    • 尽可能使用小位宽
    • 所以将主要列表分为3组:小/中/大
    • 如果你还想要bigints将它们添加为第4个列表