我目前正试图为一个Kattis问题实现一个版本的eratosthenes筛,但是,我遇到了一些我的实现无法通过的内存限制
这里是问题statement的链接。简言之,问题要求我首先返回小于或等于n的素数,然后解决一定数量的查询,如果一个数i是否为素数。有一个50 MB内存使用的限制,以及仅使用python的标准库(无numpy等)。内存限制就是我被卡住的地方
以下是我目前的代码:
import sys
def sieve_of_eratosthenes(xs, n):
count = len(xs) + 1
p = 3 # start at three
index = 0
while p*p < n:
for i in range(index + p, len(xs), p):
if xs[i]:
xs[i] = 0
count -= 1
temp_index = index
for i in range(index + 1, len(xs)):
if xs[i]:
p = xs[i]
temp_index += 1
break
temp_index += 1
index = temp_index
return count
def isPrime(xs, a):
if a == 1:
return False
if a == 2:
return True
if not (a & 1):
return False
return bool(xs[(a >> 1) - 1])
def main():
n, q = map(int, sys.stdin.readline().split(' '))
odds = [num for num in range(2, n+1) if (num & 1)]
print(sieve_of_eratosthenes(odds, n))
for _ in range(q):
query = int(input())
if isPrime(odds, query):
print('1')
else:
print('0')
if __name__ == "__main__":
main()
到目前为止,我已经做了一些改进,比如只保留一个所有奇数的列表,这将使内存使用减半。我还确信,在计算素数时,代码会按预期工作(不会得到错误的答案)。我现在的问题是,如何使我的代码更高效地使用内存?我应该使用其他数据结构吗?用布尔值替换我的整数列表?位数组
任何建议都将不胜感激
在对python中的代码进行了一些调整之后,我遇到了一个难题,我的分段筛选实现无法满足内存需求
相反,我选择用Java实现解决方案,这几乎不费什么力气。代码如下:
public int sieveOfEratosthenes(int n){
sieve = new BitSet((n+1) / 2);
int count = (n + 1) / 2;
for (int i=3; i*i <= n; i += 2){
if (isComposite(i)) {
continue;
}
// Increment by two, skipping all even numbers
for (int c = i * i; c <= n; c += 2 * i){
if(!isComposite(c)){
setComposite(c);
count--;
}
}
}
return count;
}
public boolean isComposite(int k) {
return sieve.get((k - 3) / 2); // Since we don't keep track of even numbers
}
public void setComposite(int k) {
sieve.set((k - 3) / 2); // Since we don't keep track of even numbers
}
public boolean isPrime(int a) {
if (a < 3)
return a > 1;
if (a == 2)
return true;
if ((a & 1) == 1)
return !isComposite(a);
else
return false;
}
public void run() throws Exception{
BufferedReader scan = new BufferedReader(new InputStreamReader(System.in));
String[] line = scan.readLine().split(" ");
int n = Integer.parseInt(line[0]); int q = Integer.parseInt(line[1]);
System.out.println(sieveOfEratosthenes(n));
for (int i=0; i < q; i++){
line = scan.readLine().split(" ");
System.out.println( isPrime(Integer.parseInt(line[0])) ? '1' : '0');
}
}
我个人还没有找到用Python实现此位集解决方案的方法(仅使用标准库)
如果有人在python中偶然发现了一个解决这个问题的简洁实现,使用分段筛、位数组或其他方法,我很想看看解决方案
我学到了一个技巧just yesterday——如果你把数字分成6组,6组中只有2个可能是素数。其他的可以被2或3平分。这意味着跟踪6个数字的素性只需要2位;一个包含8位的字节可以跟踪24个数字的素数!这大大减少了筛的内存需求
在Windows10上的Python3.7.5 64位中,以下代码没有超过36.4MB
编辑:了解其工作原理的关键是,筛子会创建重复模式。对于素数2和3,模式每2*3或6个数字重复一次,在这6个数字中,4个已经被渲染为不可能是素数,只剩下2个。没有什么限制你选择素数来生成模式,也许除了收益递减定律。我决定尝试在混音中加入5,使图案每2*3*5=30个数字重复一次。在这30个数字中,只有8个可以是素数,这意味着每个字节可以跟踪30个数字,而不是上面的24个!这使您在内存使用方面有20%的优势。
这是更新后的代码。我还简化了一点,去掉了素数的计数
这的确是一个非常具有挑战性的问题。最大可能的
N
为10^8,假设没有任何开销,每个值使用一个字节将导致几乎100 MB的数据。即使只存储奇数而将数据减半,在考虑开销后,也会使您的数据接近50MB这意味着解决方案必须使用以下几种策略中的一种或多种:
bytearray
李>我最初试图通过在筛选中只存储每个值1位来解决这个问题,虽然内存使用率确实在要求范围内,但Python缓慢的位操作将执行时间推得太长。同时,很难找出复杂的索引以确保可靠地计算正确的位
然后,我使用bytearray实现了仅奇数的解决方案,虽然速度快了很多,但内存仍然是个问题
Bytearray奇数实现:
由此进一步减少内存,应能使其正常工作
编辑: 此外,删除2和3的倍数似乎不足以减少内存,尽管^{} 似乎表明我的使用量实际上略低于50MB。🤷♂️
我认为您可以尝试使用布尔值列表来标记其索引是否为素数:
稍后,您可以使用
is_prime
数组通过简单地检查is_prime[number] == True
来检查一个数字是否为素数如果这不起作用,请尝试segmented sieve
另外,您可能会惊讶地发现,有一种方法可以在
O(n)
而不是O(nloglogn)
中生成筛选。检查代码here相关问题 更多 >
编程相关推荐