我的Java Sieve代码很慢,并且没有按预期的时间复杂度进行扩展

时间:2018-03-22 11:45:17

标签: java multithreading time-complexity sieve-of-eratosthenes sieve

我在Java中编写了以下'segmented sieve'程序。需要筛选一系列数字,使用'筛选素数'(素数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)的复杂性的预期时间。我需要有关优化的建议并纠正其时间复杂性。

2 个答案:

答案 0 :(得分:1)

罪魁祸首是numbers.removeAll(nonPrimes)调用,对于numbers中的每个号码(以及 O(n)中的每个号码)都会搜索所有nonPrimes可能(并且还有 O(n log log last))来检查成员资格(并且nonPrimes也是非排序的)。 n numbersn = last - start的长度。

因此,不是每个非素数的 O(1) 标记,而是 O(n log log last)实际删除它,对于它们中的每一个 O(n)。因此整个上面的 O(n ^ 2)操作。

解决此问题的一种方法是使用简单数组,标记非素数。删除会破坏直接寻址能力。如果完全使用它,操作必须在线,每个数字接近 O(1)操作。这可以通过使非素数成为排序列表来实现,然后从数字中删除它们以线性方式迭代。这两项任务最简单的再次完成。

答案 1 :(得分:1)

说明

numbers.removeAll(nonPrimes);

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

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

解决方案

有一个简单的解决方法,交换您的数据结构。为HashSet 制作的O(1)等结构包含查询。由于您只需要在addremoveAllnumbers,因此请考虑使用HashSet,而O(1)会在Set<Integer> numbers = new HashSet<>(); 中运行(已转移)。

仅更改代码:

removeAll

另一种可能性是进行一些算法更改。最后,您可以通过在收集元素时标记元素来避免Integer。优点是你可以使用数组。那么最大的优点是你避免使用盒装int类并直接运行原语primeNumbers,这些原语更快且不占用太多空间。检查@Will_Ness的答案,了解有关此方法的详细信息。

注意

您的countDat = df['country'].value_counts() 变量从未在您的方法中使用过。考虑删除它。