将变量存储为单个位

时间:2016-02-05 10:50:45

标签: python memory integer bits

过去几周我一直在努力制定一个能够使主要螺旋尽可能高效的程序。我研究了多线程以提高程序的速度,现在我遇到了一个新问题。我的素数列表的长度为6400万和零,这个列表占用240MB的内存。因为我使用多处理(总共5个进程),我的脚本最多使用总共1.1GB的ram,如果达到这一点,则返回内存错误。

关于我如何存储素数的一些背景信息:质数存储在列表中,每次我找到素数时,我将值设置为1(例如:Primes [13] = 1(因为它是素数) ,和Primes [14] = 0)。对我来说,这似乎是最好的解决方案,因为列表不会使用大量内存

经过一些基本数学后,我得出结论,我的素数列表中的每个零或一个占用4个字节(32位)的信息。这似乎是合乎逻辑的,但我想知道是否有办法将零和1存储为单个位,因此它不会占用太多内存。

提前感谢您的回答, 问候,伤害

2 个答案:

答案 0 :(得分:3)

如果每0或1取32位,则表示它的字符(可能是整数?)数组。您应该使用布尔类型(bool)代替。最简单的方法是使用bitarray。其中一个实现:

https://pypi.python.org/pypi/bitarray/0.8.1

答案 1 :(得分:0)

这里有一些Python 2代码,它创建一个打包成字节的素数文件,每个字节编码30个数字块的素数,​​利用所有素数>的事实。 5是互质的30,因此与(1,7,11,13,17,19,23,29)mod 30中的一个一致。

sieve_bin.py

#! /usr/bin/env python

''' Prime sieve.

    Save primes to a file with the primes in each block of 30 numbers 
    packed into a byte, utilising the fact that all primes > 5 are
    coprime to 30 and hence are congruent to one of 
    (1, 7, 11, 13, 17, 19, 23, 29) mod 30

    Written by PM 2Ring 2016.02.06
    Prime sieve by Robert William Hanks
    See http://stackoverflow.com/q/35222244/4014959
'''

import sys
from array import array

def rwh_primes(n):
    ''' Returns a boolean list of odd primes < n
        Adapted from code by Robert William Hanks
        See http://stackoverflow.com/a/3035188/4014959
    '''
    #Each `sieve` item represents an odd number, starting at 1
    sieve = [True] * (n//2)
    for i in xrange(3, int(n**0.5) + 1, 2):
        if sieve[i//2]:
            sieve[i*i//2::i] = [False] * ((n - i*i - 1) // (2*i) + 1)
    return sieve

def main():
    if len(sys.argv) != 2:
        print '''Generate a file of primes packed into bits.
Usage:
%s hi
to generate a file of primes < `hi`
If `hi` isn't a multiple of 30 it will be rounded down to the nearest multiple of 30
''' % sys.argv[0]
        exit()

    hi = int(sys.argv[1]) // 30 * 30
    fname = 'primebits'

    print 'Generating primes less than %d...' % hi
    odd_primes = rwh_primes(hi)

    print 'Packing primes into bytes...'
    prime_residues = (1, 7, 11, 13, 17, 19, 23, 29)
    bitmasks = [(1<<j, (u - 1) // 2) for j, u in enumerate(prime_residues)]
    packer = (sum(mask for mask, r in bitmasks if odd_primes[i + r])
        for i in xrange(0, hi//2, 15))

    primebytes = array('B', packer)
    with open(fname, 'wb') as f:
        primebytes.tofile(f)
    print 'Saved to', fname


if __name__ == '__main__':
    main()

这是一个读取primebits创建的sieve_bin.py文件的程序。该文件被读入array无符号字节,因此它在RAM使用方面有效:从文件读取每个数据字节一个字节加上array对象本身的小开销(28 32位机器上的字节数。)

此程序的main函数使用isprime函数创建素数列表。这比使用筛子慢大约4倍,但它的更少的内存开销。它可能会加速一点,但这种优化将留给读者练习。 :)

isprime_bin.py

#! /usr/bin/env python

''' Test if a number is prime, using a table read from disk
    The table contains the primes packed into bytes, with
    each byte encoding the primality of a block of 30 numbers,
    utilising the fact that all primes > 5 are coprime to 30 and
    hence are congruent to one of (1, 7, 11, 13, 17, 19, 23, 29) mod 30

    See http://stackoverflow.com/q/35222244/4014959
'''

import sys
import os.path
from array import array

def load_primes(fname):
    primebytes = array('B')
    filesize = os.path.getsize(fname)  
    with open(fname, 'rb') as f:
        primebytes.fromfile(f, filesize)

    return primebytes

prime_residues = (1, 7, 11, 13, 17, 19, 23, 29)
#Build a dict for fast conversion of residue to bitmask
bitmasks = dict((v, 1<<i) for i, v in enumerate(prime_residues))

def isprime(n, primebytes):
    if n < 2:
        return False
    if n in (2, 3, 5):
        return True
    r = n % 30
    if r not in bitmasks:
        return False
    b = primebytes[n // 30]
    return b & bitmasks[r]

#Test
def main():
    m = int(sys.argv[1]) if len(sys.argv) > 1 else 300

    fname = 'primebits'
    primebytes = load_primes(fname)
    primes = [i for i in xrange(m) if isprime(i, primebytes)]
    print primes


if __name__ == '__main__':
    main()

在我的旧单核2GHz机器上使用2GB内存时,sieve_bin.py需要大约26秒才能为数字<{1}}创建primebits文件。 64000020(文件大小= 2133334字节);大约一半的时间用于筛选素数。 isprime_bin.py大约需要124秒来生成该文件的素数列表(将输出发送到/ dev / null)。

通过将其与传统的Eratosthenes筛选程序产生的输出进行比较来验证输出。

isprime_bin.py中的代码旨在测试任意正整数的素数。要简单地生成素数列表,它可以大大加快,因为前两个if测试仅适用于数字&lt; = 5.这里是一个修改版本,需要大约47秒来生成列表来自2133334字节primebits文件的所有素数。

#! /usr/bin/env python

import sys
import os.path
from array import array

def load_primes(fname):
    primebytes = array('B')
    filesize = os.path.getsize(fname)  
    with open(fname, 'rb') as f:
        primebytes.fromfile(f, filesize)

    return primebytes

prime_residues = (1, 7, 11, 13, 17, 19, 23, 29)
#Build a dict for fast conversion of residue to bitmask
bitmasks = dict((v, 1<<i) for i, v in enumerate(prime_residues))

def isprime(n, primebytes):
    r = n % 30
    if r not in bitmasks:
        return False
    b = primebytes[n // 30]
    return b & bitmasks[r]

def primes(primebytes):
    s = (2, 3, 5) + prime_residues[1:]
    for i in s:
        yield i
    s = prime_residues
    j = 30
    while True:
        try:
            for i in s:
                p = j + i
                if isprime(p, primebytes):
                    yield p
            j += 30
        except IndexError:
            return

#Test
def main():
    m = int(sys.argv[1]) if len(sys.argv) > 1 else 300

    fname = 'primebits'
    primebytes = load_primes(fname)
    primelist = []
    for p in primes(primebytes):
        if p > m:
            break
        primelist.append(p)
    print primelist


if __name__ == '__main__':
    main()