【问题标题】:Handling memory usage for big calculation in python在 python 中处理大计算的内存使用
【发布时间】:2011-08-27 19:27:04
【问题描述】:

我正在尝试使用 python 进行一些计算,但内存不足。因此,我想读/写一个文件以释放内存。我需要一个非常大的列表对象之类的东西,所以我想为文件中的每个对象写一行并读/写该行而不是内存。行排序对我来说很重要,因为我将使用行号作为索引。所以我想知道如何在不移动其他行的情况下替换 python 中的行(实际上,移动行是可以的,只要它们返回到我期望的位置)。

编辑

我正在尝试帮助一个朋友,这在 python 中比我差或等于我。该代码应该找到最大的素数,除以给定的非素数。此代码适用于数字,直到数字达到 100 万,但死后,我的记忆力在尝试制作数字列表时耗尽。

# a comes from a user input
primes_upper_limit = (a+1) / 2
counter = 3L
numbers = list()
while counter <= primes_upper_limit:
    numbers.append(counter)
    counter += 2L

counter=3
i=0
half = (primes_upper_limit + 1) / 2 - 1
root = primes_upper_limit ** 0.5
while counter < root:
    if numbers[i]:
        j = int((counter*counter - 3) / 2)
        numbers[j] = 0
        while j < half:
            numbers[j] = 0
            j += counter
    i += 1
    counter = 2*i + 3
primes = [2] + [num for num in numbers if num]
for numb in reversed(primes):
    if a % numb == 0:
        print numb
        break
另一个编辑

为每个索引写入不同的文件怎么样?例如十亿个长整数文件名的文件,文件中只有一个数字?

【问题讨论】:

  • 让我们解决内存不足的原因:告诉我们计算,还是发布代码?你也可以试试 NumPy。
  • 如果您解释您要解决的实际问题,您可能会得到更好的答案。
  • 我在原始问题中添加了更多解释和代码。
  • 我建议不要将文件系统带入其中。你真的想要十亿个文件吗?会很慢。如果你真的想用磁盘来增加你的内存,你可以制作一个交换文件。
  • 您是否需要存储所有质因数的列表,然后选择最大的(如您所做的那样)?还是直接找出最大的质因数?

标签: python stack-overflow sieve-of-eratosthenes prime-factoring


【解决方案1】:

您想找到 a 的最大素数除数。 (Project Euler Question 3) 您当前选择的算法和实现方式是:

  1. 生成范围内所有候选素数的列表numbers(3
  2. 筛选numbers 列表以获得素数列表 {p}
  3. 试用除法:测试a被每个p整除。存储 a 的所有素数除数 {q}。
  4. 打印所有除数 {q};我们只想要最大的。

我在这个算法上的 cmets 如下。正如 Owen 和我所评论的那样,筛分和试验划分是严重不可扩展的算法。对于较大的 a(十亿或万亿),您确实应该使用 NumPy。无论如何,一些关于实现这个算法的 cmets:

  1. 您是否知道您只需要测试到 √aint(math.sqrt(a)),而不是像您那样测试 (a+1)/2?
  2. 没有必要建立一个庞大的候选人名单numbers,然后将其筛选为素数 - 数字列表不可扩展。只需直接构造列表primes。您可以使用 while/for-loops 和 xrange(3,sqrt(a)+2,2)(它为您提供了一个迭代器)。正如您在 2**31L 中提到的 xrange() 溢出,但结合 sqrt 观察,您仍然可以成功分解至 2**62
  3. 一般来说,这不如得到 a 的素数分解,即每次找到素数除数 p | a,您只需要继续筛选剩余的因子 a/p 或 a/p² 或 a/p³ 或其他)。除了非常大的素数(或伪素数)这种罕见的情况外,这将大大减少您正在处理的数字的大小。
  4. 另外,您只需要生成一次素数 {p} 列表;此后存储它并进行查找,而不是重新生成它。 所以我会将generate_primes(a)find_largest_prime_divisor(a) 分开。分解有很大帮助。

这是我对您的代码的重写,但由于保留了筛选列表,性能仍然下降了数十亿 (a > 10**11 +1)。我们可以使用 collections.deque 代替 list 来获取素数,以获得更快的 O(1) append() 操作,但这是一个小的优化。

# Prime Factorization by trial division

from math import ceil,sqrt
from collections import deque

# Global list of primes (strictly we should use a class variable not a global)
#primes = deque()
primes = []

def is_prime(n):
    """Test whether n is divisible by any prime known so far"""
    global primes
    for p in primes:
         if n%p == 0:
             return False #  n was divisible by p
    return True # either n is prime, or divisible by some p larger than our list    
def generate_primes(a):
    """Generate sieved list of primes (up to sqrt(a)) as we go"""
    global primes
    primes_upper_limit = int(sqrt(a))
    # We get huge speedup by using xrange() instead of range(), so we have to seed the list with 2
    primes.append(2)
    print "Generating sieved list of primes up to", primes_upper_limit, "...",
    # Consider prime candidates 2,3,5,7... in increasing increments of 2
    #for number in [2] + range(3,primes_upper_limit+2,2):
    for number in xrange(3,primes_upper_limit+2,2):
        if is_prime(number): # use global 'primes'
            #print "Found new prime", number
            primes.append(number) # Found a new prime larger than our list
    print "done"    
def find_largest_prime_factor(x, debug=False):
    """Find all prime factors of x, and return the largest."""
    global primes
    # First we need the list of all primes <= sqrt(x)    
    generate_primes(x)
    to_factor = x # running value of the remaining quantity we need to factor
    largest_prime_factor = None
    for p in primes:
        if debug: print "Testing divisibility by", p
        if to_factor%p != 0:
            continue
        if debug: print "...yes it is"
        largest_prime_factor = p
        # Divide out all factors of p in x (may have multiplicity)
        while to_factor%p == 0:
            to_factor /= p
        # Stop when all factors have been found
        if to_factor==1:
            break
    else:
        print "Tested all primes up to sqrt(a), remaining factor must be a single prime > sqrt(a) :", to_factor
    print "\nLargest prime factor of x is", largest_prime_factor
    return largest_prime_factor

【讨论】:

  • 我认为numbers 的目的是存储素数——它就像埃拉托色尼的筛子。
  • @Owen:当然,但没有必要将所有内容存储为列表,而不是set。我们应该给他一个非常糟糕(且不可扩展)算法的字面实现,还是帮助他理解什么会使它成为一个更好的算法?
  • 对不起,我忘了提到 range/xrange 开始的问题不接受长对象。而且由于我是用筛子计算素数,所以不列出清单是行不通的。而且这种计算不使用筛子真的很慢。
  • @yasar:但正如我指出的那样,您只需要测试直到 sqrt(a) 的素数,而不是 (a+1)/2。如果 a >= 2**31,你只需要多头
  • @yasar11732 我想这将取决于a 的规模有多大——你只是达到数十亿吗?因为那你应该没事。如果你要进入数万亿,情况会更糟,但即便如此,你也会有一个万亿长的筛子。
【解决方案2】:

如果我的理解正确,这不是一件容易的事。他们按照我的解释,你想保持文件句柄打开,并将文件用作存储字符数据的地方。

假设你有一个类似的文件,

a
b
c

并且您想将“b”替换为“bb”。这会很痛苦,因为文件实际上看起来像a\nb\nc——你不能只覆盖b,你需要另一个字节。

我的建议是尝试找到一种方法来使您的算法正常工作,而无需使用文件进行额外存储。如果您遇到堆栈溢出,很可能您并没有真正耗尽内存,而是超出了调用堆栈,该堆栈要小得多

您可以尝试重新设计您的算法,使其不再是递归的。有时您可以使用list 来代替调用堆栈——但是您可以做很多事情,如果没有看到您的算法,我认为我无法给出太多一般性的建议。

编辑

啊我明白你的意思了...当列表

while counter <= primes_upper_limit:
    numbers.append(counter)
    counter += 2L

变得非常大,你可能会耗尽内存。所以我猜你基本上是在做筛子,这就是为什么你有大名单numbers?这说得通。如果您想继续这样做,您可以尝试使用 numpy bool 数组,因为每个单元格使用的内存会大大减少:

import numpy as np

numbers = np.repeat(True, a/2)

或者(也许这并不吸引人)您可以采用不使用大列表的完全不同的方法,例如完全分解数字并选择最大的因素。

类似:

factors = [ ]
tail = a

while tail > 1:
    j = 2
    while 1:
        if tail % j == 0:
            factors.append(j)
            tail = tail / j
            print('%s %s' % (factors, tail))
            break
        else:
            j += 1

例如说你在考虑20tail20 开头,然后你发现2 tail 变成10,然后它变成5

这不是很有效,并且对于大(十亿)质数来说会变得方式太慢,但对于小因数的数字来说没关系。

我的意思是你的筛子也很好,直到你开始用尽内存;)。你可以试试numpy

【讨论】:

  • 我在原始问题中添加了算法。
  • 我不是以英语为母语的人,我不太明白你所说的完全分解数字并选择最大因素的意思。
  • 对不起。我的意思是,从a 开始,将其分解为b * c,将c 分解为d * e,等等。
  • 您能否链接到一个示例算法以完全分解该数字。我不是算法专家,也不知道该怎么做。
  • 这就叫找a的Integer Factorization or Prime Decomposition。在这种情况下,您使用的是 Trial Division 方法(这是最简单的方法)。
【解决方案3】:

pytables 非常适合处理和存储大量数据。但首先从实现 smci 答案中的 cmets 开始,以尽量减少您需要存储的数字量。

【讨论】:

    【解决方案4】:

    对于只有 12 位数字的数字,如 Project Euler #3,不需要花哨的整数分解方法,也不需要将中间结果存储在磁盘上。用这个算法求n的因数:

    1. 设置 f = 2。
    2. 如果 n = 1,则停止。
    3. 如果 f * f > n,打印 n 并停止。
    4. n 除以 f,同时保留商 q 和余数 r。
    5. 如果 r = 0,则打印 q,将 n 除以 q,然后转到步骤 2。
    6. 否则,将 f 增加 1 并转到步骤 3。

    这只是对每个整数进行试除,直到它达到平方根,这表明剩余的辅因子是素数。每个因子都会在找到时打印出来。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-08-28
      • 1970-01-01
      • 2013-01-22
      • 1970-01-01
      • 2017-01-14
      • 2016-06-27
      • 2012-11-26
      • 1970-01-01
      相关资源
      最近更新 更多