【问题标题】:Java implementation of Sieve of Eratosthenes that can go past n = 2^32?可以超过 n = 2^32 的 Eratosthenes 筛的 Java 实现?
【发布时间】:2023-03-24 23:40:01
【问题描述】:

目前我有这个限制为 n

筛子:

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?

【问题讨论】:

  • 创建另一个数组? :)
  • @alfasin 有没有更好的方法来做到这一点?比如以编程方式创建更多空间,可能是在二维数组中?
  • 用例是什么?我用过很多次筛子,从来没有真正需要过尺寸 > 10^7
  • @vish4071 这只是我一直在做的一个项目。
  • 项目很好。我的意思是,筛子实现的瓶颈是它需要大量的内存。大多数(通用)语言并没有提供那么多。即使使用boolean 数组来计算最多 2^32 个素数也不错,但肯定会花费很多时间。 (复杂度是O(n loglog n),是最优化的实现,我见过的大部分实现复杂度都是O(n log n))

标签: java algorithm primes sieve-of-eratosthenes


【解决方案1】:

您可以使用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,这是低于 40 亿的正确素数 according to Wolfram Alpha。如果你有足够的内存,你可以尝试设置更大的 N。

【讨论】:

    【解决方案2】:

    您可以对筛子进行分段:您无需分配单个巨大的数组,而是分配许多小数组。如果您想找到最大 10^10 的素数,您可以使用大小为 10^6 左右的数组(或者更好的是,BitSets)。然后你运行筛子 10^4 次。每次运行一个新片段时,您都需要找出筛子中每个素数的起始位置,但这并不难。

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

    【讨论】:

      【解决方案3】:

      我看到了选项:

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

        • 仅记住每个位的奇数
        • 使用无符号变量避免符号位浪费
      2. 使用超过 1 个表格

        • 但在 32 位应用程序中,您会受到操作系统功能的限制
        • 通常为 1/2/4 GB 的可用内存
        • 这么大的表通常放不下 CACHE,所以速度不是很快
      3. 您可以一次使用更多方法

        • 我将周期筛与找到的素数列表二分搜索结合起来
        • 如果您选择正确的尺寸,您甚至可以提高性能
        • 通过更好地使用平台缓存属性
        • Prime numbers by Eratosthenes quicker sequential than concurrently?
        • 这个想法是使用筛子来测试小的除数
        • 然后才检查主要列表中的存在
        • 它不需要那么多内存
        • 而且速度很快
      4. 为了节省内存,您可以组合 16/32/64 位变量

        • 尽可能使用小位宽
        • 因此将主要列表分为 3 组:小/中/大
        • 如果您还想要 bigint,请将它们添加为第 4 个列表

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-04-14
        • 1970-01-01
        • 2017-01-11
        • 2018-02-11
        • 1970-01-01
        相关资源
        最近更新 更多