繁体   English   中英

我的Java Sieve代码很慢,无法按预期的时间复杂度进行扩展

[英]My Java Sieve code is slow and not scaling at the expected time complexity

我用Java编写了以下“分段筛”程序。 筛选需要一定范围的数字,使用“筛除质数”(质数arraylist变量)将合成数相除,然后返回尚未被相除的质数。 这是代码:

public ArrayList<Integer> sieveWorker(int start, int last, ArrayList<Integer> primes) {

    System.out.println("Thread started for range: " + start + "-" + last);
    ArrayList<Integer> nonPrimes = new ArrayList<Integer>();
    ArrayList<Integer> primeNumbers = new ArrayList<Integer>();
    ArrayList<Integer> numbers = new ArrayList<Integer>();

    //numbers to be sieved
    for (int i = start; i <= last; i += 2) {
        numbers.add(i);
    }

    //identifies composites of the sieving primes, then stores them in an arraylist
    for (int i = 0; i < primes.size(); i++) {

        int head = primes.get(i);

        if ((head * head) <= last) {
            if ((head * head) >= start) {
                for (int j = head * head; j <= last; j += head * 2) {
                    nonPrimes.add(j);
                }
            } else {
                int k = Math.round((start - head * head) / (2 * head));
                for (int j = (head * head) + (2 * k * head); j <= last; j += head * 2) {
                    nonPrimes.add(j);
                }
            }
        }

    }

    numbers.removeAll(nonPrimes);
    System.out.println("Primes: " + numbers);
    return numbers;
}

我的问题是它非常慢,并且执行时的复杂度为o(n ^ 3),而不是预期的复杂度为o(n log log n)。 我需要有关优化和纠正其时间复杂度的建议。

罪魁祸首是numbers.removeAll(nonPrimes)调用,它针对数字中的每个numbers (并且有O(n)个)潜在地搜索所有非nonPrimes (并且它们中有O(n个log log个) )以进行检查成员资格( nonPrimes也未排序)。 nnumbers的长度, n = last - start

因此,代替每个非质数的O(1) 标记 ,您可以对它们中的每个O(n)进行O(n log log last)实际删除。 因此,上述O(n ^ 2)操作总体而言。

解决此问题的一种方法是使用简单的数组,并标记非素数。 删除会破坏直接地址功能。 如果完全使用它,则操作必须在线 ,每个数字接近O(1)个操作。 这可以通过使非素数成为有序列表,然后将其从线性迭代的数字中删除来实现。 同样,这两个任务最容易用数组完成。

说明

numbers.removeAll(nonPrimes);

必须找到元素。 基本上, 包含包含ArrayList上的速度很慢, O(n)

它从左到右迭代整个列表,并删除匹配的元素。 它会对nonPrimes集合中的每个元素执行此操作。 因此,仅对于removeAll部分,您将获得O(n * |nonPrimes|)的复杂度。


有一个简单的解决方法,可以交换您的数据结构。 O(1)构造的HashSet结构包含查询。 因为你只需要addremoveAllnumbers ,可以考虑使用HashSet相反,它运行在两个O(1) (ammortized)。

仅更改代码:

Set<Integer> numbers = new HashSet<>();

另一种可能性是进行一些算法更改。 您可以通过在收集元素时标记元素来最终避免removeAll 好处是您可以使用数组。 这样做的最大好处是,您可以避免使用盒装的Integer类,而直接在原语int上运行,而原语int更快,并且不会占用太多空间。 有关此方法的详细信息,请检查@Will_Ness的答案。


注意

您的primeNumbers变量从未在您的方法中使用。 考虑删除它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM