【问题标题】:Exceeding the size of lists in python超过python中列表的大小
【发布时间】:2010-02-25 21:21:05
【问题描述】:

我正在尝试在 python 中实现sieve of eratosthenes,但是当尝试查找直到例如779695003923747564589111193840021 的平方根的所有素数时,我收到一条错误消息,指出 range() 的结果有太多项目。我的问题是,如何避免这个问题,如果我用 while 循环实例化列表,我会收到一个错误,说我使用了太多内存(甚至在它开始使用页面文件之前),下面列出了这两个:

使用范围()

maxnum = 39312312323123123

primes = []
seq = []
i = 0
seq = range(2,maxnum)

for i in seq:
    mul = i * seq
    for j in mul:
        try:
            seq.remove(j)
        except:
            pass
        primes.append(i)

print primes

使用while:

maxnum = 39312312323123123

primes = []
seq = []
i = 0
while i < maxnum:
    seq.append(i)
    i+=1

for i in seq:
    mul = i * seq
    for j in mul:
        try:
            seq.remove(j)
        except:
            pass
        primes.append(i)

print primes

【问题讨论】:

  • 是的,这是一个学校作业,但我很确定我们应该发现如果不使用 Wolfram Alpa 之类的工具,分解 779695003923747564589111193840021 是非常困难的,但我想尽我所能尝试我的其他(当前)素数查找算法现在已经运行了 22 小时,但没有给我想要的东西。
  • 在这台笔记本电脑上输入 "factor(779695003923747564589111193840021)" 到 Maxima 会在大约 3 秒内产生输出:43*167*9059*1510775033423*7933407561613。我认为如果没有 64 位和大量内存和时间,您将无法通过筛选获得这两个最大的。 OTOH,这就是重点吗?
  • except: pass 总是错误的。改为捕获实际异常 (ValueError)。

标签: python memory prime-factoring sieve-of-eratosthenes


【解决方案1】:

我会说,“改用xrange()”,但您实际上是使用整数列表作为筛分结果.....所以整数生成器不是正确的解决方案。

我认为很难实现一个包含 39312312323123123 个元素的列表,无论您使用什么函数来实现......毕竟,279 PB 的 64 位整数。

试试这个。

class FoundComposite(Exception): pass

primes = [2]

seq = itertools.takewhile(        # Take integers from a list
          lambda x: x<MAXNUM,     #   until we reach MAXNUM
          itertools.count(2)      #   the list of integers starting from 2
          )

#seq = xrange(2, MAXNUM)          # alternatively

for i in seq:
    try:
        for divisor in primes:
            if not (i % divisor):
                # no remainder - thus an even divisor
                # continue to next i in seq
                raise FoundComposite 
        # if this is reached, we have tried all divisors.
        primes.append(i)
    except FoundComposite:
        pass

【讨论】:

  • xrange 失败并出现与 range 相同的 MemoryError,筛子的重点是您需要拥有所有元素,因此您的其他想法也行不通:(
  • 天哪,我需要多读一点算法。对不起。我看到你正在做一个筛子,需要构造的整数列表。
  • xrange 可能会绕过实例化具有大量元素的列表的限制,但原样的代码会很快崩溃。例如“mul = i * seq”将不起作用。它适用于普通列表,但您正在制作列表的“i”个副本。不知道那里的意图是什么。我会在这里寻找一些灵感:code.activestate.com/recipes/117119-sieve-of-eratosthenes 这些都是一些非常聪明的人。
  • 您在那里嵌套了 for 循环,我相信 continue 只会增加内部 for 循环。我最终使用了一个标志和短路来做到这一点。
  • +1 表示 pythonicity (python-ness?),比布尔标志更清晰,尽管它可能会慢一些,因为它经常被提升。
【解决方案2】:

这是一种更复杂的算法,在技术上可能不算作筛子,但一种方法是不要一次删除给定素数的所有倍数,而是将下一个倍数(与素数一起)排队。这可以在生成器实现中使用。队列最终仍会包含很多(多个)素数,但不如构建然后过滤列表那么多。

前几步手动完成,展示原理...

  • 2 是素数 - 产量和队列 (4, 2)
  • 3 是素数 - 产量和队列 (6, 3)
  • 4 是复合的 - 将队列中的 (4, 2) 替换为 (6, 2)
  • 5 是素数 - 产量和队列 (10, 5)
  • 6 是复合的 - 将 (6, 2) 替换为 (8, 2) 并将 (6, 3) 替换为 (9, 3)

注意 - 队列不是 FIFO。您将始终提取具有最低第一项的元组,但新/替换元组(通常)没有最高的第一项,并且(与上面的 6 一样)会有重复。

为了在 Python 中有效地处理队列,我建议使用以元组的第一项为键的字典(即哈希表)。数据是一组第二项值(原始素数)。

正如其他地方所建议的,在尝试大目标之前先测试小目标。如果你失败了也不要太惊讶。可能仍然是您一次需要太多堆分配的大整数(在队列中)才能完成解决方案。

【讨论】:

  • 我确实最终使用了这个解决方案,它非常快,是的,我最终再次超过了限制,我想这不应该在 python 中:(
  • 老实说,我没想到它会很快 - 只是需要更少的内存。
【解决方案3】:

你的算法坏了。首先让它为 maxnum=100 工作。

一旦你开始工作,你会发现 maxnum=100000000 需要很长时间才能运行。

绘制在 (10,100,1000,10000,100000,1000000...) 中运行 maxnum 所需的时间,您可以推断出 39312312323123123 需要多长时间 :)

【讨论】:

  • 我的另一个发布的生成器在几分钟内达到 100000000,这应该很快,当然我应该先检查算法,但这是我目前最少的问题;)我会更新它直到它工作
【解决方案4】:

有一个python的第三方模块叫做gmpy

它有几个可能对您有用的功能,因为它们非常快。概率的东西大约在 40 亿大关。

next_prime(...)
    next_prime(x): returns the smallest prime number > x.  Note that
    GMP may use a probabilistic definition of 'prime', and also that
    if x<0 GMP considers x 'prime' iff -x is prime; gmpy reflects these
    GMP design choices. x must be an mpz, or else gets coerced to one.

is_prime(...)
    is_prime(x,n=25): returns 2 if x is _certainly_ prime, 1 if x is
    _probably_ prime (probability > 1 - 1/2**n), 0 if x is composite.
    If x<0, GMP considers x 'prime' iff -x is prime; gmpy reflects this
    GMP design choice. x must be an mpz, or else gets coerced to one.

【讨论】:

    【解决方案5】:

    range() 返回一个包含请求范围内所有数字的列表,而xrange 是一个生成器,它一个接一个地生成数字,内存消耗接近于零。

    【讨论】:

    • 我尝试了 xrange,但我需要能够从列表中删除 xrange 不允许我这样做。
    【解决方案6】:

    关于内存限制,如何创建一个内部是列表或数组的链表的自定义列表(类)。在内部神奇地从一个遍历到另一个,并根据需要添加更多,因为调用者使用您的自定义列表和您提供的外部接口,这将类似于促进 .append .remove 等需要的那些成员问题中使用的数组。

    注意:我不是 Python 程序员。不知道如何实现我在 Python 中所说的内容。也许我不知道这里的上下文,所以如果我被否决了,我会理解的。

    也许使用“generators”,因为它们在 python 中被调用来产生内部列表的结果,就好像它是一个巨大的单个列表一样。可能是linked list

    【讨论】:

      【解决方案7】:

      试试这个:

      def getPrimes(maxnum):
          primes = []
          for i in xrange(2, maxnum):
              is_mul = False
              for j in primes:         # Try dividing by all previous primes
                  if i % j == 0:
                      is_mul = True    # Once we find a prime that i is divisible by
                      break            # short circuit so we don't have to try all of them
              if not is_mul:           # if we try every prime we've seen so far and `i`
                  primes.append(i)     # isn't a multiple, so it must be prime
          return primes
      

      在获得大量素数之前,不应耗尽内存。这样您就不必担心创建倍数列表。不确定这是否仍然算作筛子。

      实际上,这不适用于maxnum = 39312312323123123。使用Prime number theorem,我们可以估计在该范围内大约有1,028,840,332,567,181 素数。

      正如this question 中指出的那样,32 位系统上 python 列表的最大大小是536,870,912。所以即使你没有耗尽内存,你仍然无法完成计算。

      不过,64 位系统不应该有这个问题。

      2 ** 64 =&gt; 18446744073709551616

      使用上述问题(2 ** 64) / 8 的数学运算,列表中的最大元素数将是2,305,843,009,213,693,951,这大于您将遇到的估计素数数。

      编辑:

      为避免内存问题,您可以将素数列表存储在硬盘上的文件中。每行存储一个素数,并在每次检查新数字时读取文件。

      可能是这样的:

      primes_path = r'C:\temp\primes.txt'
      
      def genPrimes():
          for line in open(primes_path, 'r'):
              yield int(line.strip())    
      
      def addPrime(prime):
          primes_file = open(primes_path, 'a')
          primes_file.write('%s\n' % prime)
          primes_file.close()
      
      def findPrimes(maxnum):
          for i in xrange(2, maxnum):
              is_mul = False
              for prime in genPrimes():  # generate the primes from a file on disk
                  if i % prime == 0:
                      is_mul = True    
                      break            
              if not is_mul:           
                  addPrime(i)  # append the new prime to the end of your primes file
      

      最后,您的硬盘驱动器上会有一个文件,其中包含您所有的质数。

      好的,所以这会很慢,但不会耗尽内存。您可以通过提高读取/写入文件的速度来加快速度(例如 RAID)。

      【讨论】:

      • 我实际上是在 64 位系统上运行,我使用我提供的其他算法遇到了内存限制。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-17
      • 2018-05-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多