【问题标题】:Report MemoryError and OverflowError arbitrarily任意上报 MemoryError 和 OverflowError
【发布时间】:2019-12-10 01:36:46
【问题描述】:

我使用“埃拉托色尼筛法”来产生素数。

def primes(n):
    if n < 2:
        return None
    primes_sieve = [True] * (n + 1) 
    primes_sieve[1] = False
    primes_sieve[0] = False

    # sieve
    for i in range(2, int(n ** 0.5) + 1):  
        if primes_sieve[i]:
            for j in range(i * i, n + 1, i):  
                primes_sieve[j] = False

    return [i for i, p in enumerate(primes_sieve) if p]

它适用于小数字。

In [28]: primes(2**10)[:10]              
Out[28]: [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
In [29]: primes(2**10)[-10:]             
Out[29]: [967, 971, 977, 983, 991, 997, 1009, 1013, 1019, 1021]
In [30]: primes(2**15)[-10:]             
Out[30]: [32633, 32647, 32653, 32687, 32693, 32707, 32713, 32717, 32719, 32749]

对于一些大的数字,任意报错。

内存错误

In [32]: primes(2**30)[-10:]             
---------------------------------------------------------------------------
MemoryError                               Traceback (most recent call last)
<ipython-input-32-3e546166a3d9> in <module>
----> 1 primes(2**30)[-10:]

<ipython-input-26-5f07bf9cb49e> in primes(n)
      2     if n < 2:
      3         return None
----> 4     primes_sieve = [True] * (n + 1)
      5     primes_sieve[1] = False
      6     primes_sieve[0] = False

MemoryError: 

和溢出错误:

In [35]: primes(2**100)[-10:]            
---------------------------------------------------------------------------
OverflowError                             Traceback (most recent call last)
<ipython-input-35-3bb8857d5f28> in <module>
----> 1 primes(2**100)[-10:]

<ipython-input-26-5f07bf9cb49e> in primes(n)
      2     if n < 2:
      3         return None
----> 4     primes_sieve = [True] * (n + 1)
      5     primes_sieve[1] = False
      6     primes_sieve[0] = False

OverflowError: cannot fit 'int' into an index-sized integer

有什么问题?我很高兴也很好奇地发现python也有OverflowError。

【问题讨论】:

    标签: python python-3.x allocation


    【解决方案1】:

    Python lists 被实现为指向 Python 对象的动态大小的 C 指针数组。当你这样做时:

    primes_sieve = [True] * (n + 1)
    

    其中n2**30,您要求分配2**32(在32 位系统上)或2**33(在64 位系统上)字节的单个连续内存。在 Python 的 32 位版本上,前者是不可能的;整个虚拟内存地址空间,用户+内核,为2**32字节,其中一些不可用,一些已经在使用中。 Python 立即拒绝请求比地址空间更多的内存。即使在 64 位系统上,系统也可能出于任何原因拒绝对那么多内存的请求(例如,如果页面文件/交换文件中没有足够的空间来处理该内存),Python 将以相同的方式报告它,如 @ 987654328@.

    OverflowError 只是告诉您您传递的值不适合所涉及的底层 C 类型。 list(在 CPython 上)将其大小存储为 signed size_tPy_ssize_ttypedef),仅限于 2**31 - 12**63 - 1;无论哪种方式,都比2 ** 100 + 1 小得多。在 CPython 甚至尝试执行分配之前,它会尝试将请求的大小提取为 signed size_t,并在无法这样做时立即死亡。

    重点是,试图使lists 的大小为2**30 总是几乎错误,而使一个大小为2**100 (它不适合世界上的任何系统) ) 从一开始就注定了。在 Python 或任何其他语言中,您不能让 Eratosthenes 的筛子对2**100 执行完整的筛子;充其量你可以筛选大约2**64 的点点滴滴(通过筛选范围顶部的平方根,然后使用这些数字筛选一小部分数字,比如你似乎正在测试的前 10 个,但不是整个范围)。

    旁注:如果您正在实施埃拉托色尼筛,减少内存消耗的第一步是替换:

    primes_sieve = [True] * (n + 1) 
    

    与:

    primes_sieve = bytearray('\x01') * (n + 1)
    

    这会将筛子中的每项成本从指向单个字节的指针降低,从而将内存使用量减少 4-8 倍。您可以通过仅存储奇数信息来进一步减半内存需求(因为除了可以硬编码的2,只有奇数可以是素数)。从那里,您可以转向使用可以提供打包位集的第三方类(将内存使用量减少 8 倍,从一个字节到一个位)。有进一步的优化可用,但大多数在 Python 中会很慢(你必须转向 C 才能看到好处);这可以为你研究。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-05-24
      • 1970-01-01
      • 1970-01-01
      • 2021-01-11
      • 1970-01-01
      • 2015-11-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多