我在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)的复杂性的预期时间。我需要有关优化的建议并纠正其时间复杂性。
答案 0 :(得分:1)
罪魁祸首是numbers.removeAll(nonPrimes)
调用,对于numbers
中的每个号码(以及 O(n)中的每个号码)都会搜索所有nonPrimes
可能(并且还有 O(n log log last))来检查成员资格(并且nonPrimes
也是非排序的)。 n 是numbers
,n = 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)
等结构包含查询。由于您只需要在add
上removeAll
和numbers
,因此请考虑使用HashSet
,而O(1)
会在Set<Integer> numbers = new HashSet<>();
中运行(已转移)。
仅更改代码:
removeAll
另一种可能性是进行一些算法更改。最后,您可以通过在收集元素时标记元素来避免Integer
。优点是你可以使用数组。那么最大的优点是你避免使用盒装int
类并直接运行原语primeNumbers
,这些原语更快且不占用太多空间。检查@Will_Ness的答案,了解有关此方法的详细信息。
您的countDat = df['country'].value_counts()
变量从未在您的方法中使用过。考虑删除它。