加速Python中的位串/位操作?

时间:2010-05-24 13:31:13

标签: python-3.x optimization bit-manipulation primes sieve-of-eratosthenes

我使用Sieve of Eratosthenes和Python 3.1编写了素数生成器。代码在ideone.com上以0.32秒正确且正常地运行,以生成高达1,000,000的素数。

# from bitstring import BitString

def prime_numbers(limit=1000000):
    '''Prime number generator. Yields the series
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...    
    using Sieve of Eratosthenes.
    '''
    yield 2
    sub_limit = int(limit**0.5) 
    flags = [False, False] + [True] * (limit - 2)   
#     flags = BitString(limit)
    # Step through all the odd numbers
    for i in range(3, limit, 2):       
        if flags[i] is False:
#        if flags[i] is True:
            continue
        yield i
        # Exclude further multiples of the current prime number
        if i <= sub_limit:
            for j in range(i*3, limit, i<<1):
                flags[j] = False
#                flags[j] = True

问题是,当我尝试生成高达1,000,000,000的数字时,内存不足。

    flags = [False, False] + [True] * (limit - 2)   
MemoryError

可以想象,在Python中分配10亿个布尔值( 1个字节 4或8个字节(请参阅注释))实际上是不可行的,所以我查看了bitstring。我想,每个标志使用1位将更加节省内存。然而,该程序的性能急剧下降 - 运行时间为24秒,素数高达1,000,000。这可能是由于bitstring的内部实现。

您可以评论/取消注释这三行,以查看我更改为使用BitString的内容,作为上面的代码段。

我的问题是,有没有办法加速我的程序,有或没有位串?

编辑:请在发布前自行测试代码。我自然不能接受比现有代码慢的答案。

再次修改:

I've compiled a list of benchmarks on my machine.

11 个答案:

答案 0 :(得分:34)

您的版本有一些小优化。通过反转True和False的角色,您可以将“if flags[i] is False:”更改为“if flags[i]:”。第二个range语句的起始值可以是i*i,而不是i*3。您的原始版本在我的系统上需要0.166秒。通过这些更改,我的系统下面的版本需要0.156秒。

def prime_numbers(limit=1000000):
    '''Prime number generator. Yields the series
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...
    using Sieve of Eratosthenes.
    '''
    yield 2
    sub_limit = int(limit**0.5)
    flags = [True, True] + [False] * (limit - 2)
    # Step through all the odd numbers
    for i in range(3, limit, 2):
        if flags[i]:
            continue
        yield i
        # Exclude further multiples of the current prime number
        if i <= sub_limit:
            for j in range(i*i, limit, i<<1):
                flags[j] = True

但这对你的记忆问题没有帮助。

进入C扩展世界,我使用了gmpy的开发版本。 (免责声明:我是维护者之一。)开发版本称为gmpy2,支持名为xmpz的可变整数。使用gmpy2和以下代码,我的运行时间为0.140秒。限制为1,000,000,000的运行时间为158秒。

import gmpy2

def prime_numbers(limit=1000000):
    '''Prime number generator. Yields the series
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...
    using Sieve of Eratosthenes.
    '''
    yield 2
    sub_limit = int(limit**0.5)
    # Actual number is 2*bit_position + 1.
    oddnums = gmpy2.xmpz(1)
    current = 0
    while True:
        current += 1
        current = oddnums.bit_scan0(current)
        prime = 2 * current + 1
        if prime > limit:
            break
        yield prime
        # Exclude further multiples of the current prime number
        if prime <= sub_limit:
            for j in range(2*current*(current+1), limit>>1, prime):
                oddnums.bit_set(j)

推动优化并牺牲清晰度,使用以下代码获得0.107和123秒的运行时间:

import gmpy2

def prime_numbers(limit=1000000):
    '''Prime number generator. Yields the series
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...
    using Sieve of Eratosthenes.
    '''
    yield 2
    sub_limit = int(limit**0.5)
    # Actual number is 2*bit_position + 1.
    oddnums = gmpy2.xmpz(1)
    f_set = oddnums.bit_set
    f_scan0 = oddnums.bit_scan0
    current = 0
    while True:
        current += 1
        current = f_scan0(current)
        prime = 2 * current + 1
        if prime > limit:
            break
        yield prime
        # Exclude further multiples of the current prime number
        if prime <= sub_limit:
            list(map(f_set,range(2*current*(current+1), limit>>1, prime)))

编辑:根据此练习,我修改了gmpy2以接受xmpz.bit_set(iterator)。使用以下代码,所有素数少于1,000,000,000的运行时间对于Python 2.7为56秒,对于Python 3.2为74秒。 (如评论中所述,xrangerange更快。)

import gmpy2

try:
    range = xrange
except NameError:
    pass

def prime_numbers(limit=1000000):
    '''Prime number generator. Yields the series
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...
    using Sieve of Eratosthenes.
    '''
    yield 2
    sub_limit = int(limit**0.5)
    oddnums = gmpy2.xmpz(1)
    f_scan0 = oddnums.bit_scan0
    current = 0
    while True:
        current += 1
        current = f_scan0(current)
        prime = 2 * current + 1
        if prime > limit:
            break
        yield prime
        if prime <= sub_limit:
            oddnums.bit_set(iter(range(2*current*(current+1), limit>>1, prime)))

编辑#2:再试一次!我修改了gmpy2以接受xmpz.bit_set(slice)。使用以下代码,Python 2.7和Python 3.2的所有质数小于1,000,000,000的运行时间约为40秒。

from __future__ import print_function
import time
import gmpy2

def prime_numbers(limit=1000000):
    '''Prime number generator. Yields the series
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...
    using Sieve of Eratosthenes.
    '''
    yield 2
    sub_limit = int(limit**0.5)
    flags = gmpy2.xmpz(1)
    # pre-allocate the total length
    flags.bit_set((limit>>1)+1)
    f_scan0 = flags.bit_scan0
    current = 0
    while True:
        current += 1
        current = f_scan0(current)
        prime = 2 * current + 1
        if prime > limit:
            break
        yield prime
        if prime <= sub_limit:
            flags.bit_set(slice(2*current*(current+1), limit>>1, prime))

start = time.time()
result = list(prime_numbers(1000000000))
print(time.time() - start)

编辑#3:我已经更新了gmpy2以正确支持xmpz位级别的切片。性能没有变化,但是API非常好。我做了一点调整,我的时间缩短了大约37秒。 (请参阅编辑#4以了解gmpy2 2.0.0b1中的更改​​。)

from __future__ import print_function
import time
import gmpy2

def prime_numbers(limit=1000000):
    '''Prime number generator. Yields the series
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...
    using Sieve of Eratosthenes.
    '''
    sub_limit = int(limit**0.5)
    flags = gmpy2.xmpz(1)
    flags[(limit>>1)+1] = True
    f_scan0 = flags.bit_scan0
    current = 0
    prime = 2
    while prime <= sub_limit:
        yield prime
        current += 1
        current = f_scan0(current)
        prime = 2 * current + 1
        flags[2*current*(current+1):limit>>1:prime] = True
    while prime <= limit:
        yield prime
        current += 1
        current = f_scan0(current)
        prime = 2 * current + 1

start = time.time()
result = list(prime_numbers(1000000000))
print(time.time() - start)

编辑#4:我在gmpy2 2.0.0b1中做了一些改动,打破了前面的例子。 gmpy2不再将True视为提供1位无限来源的特殊值。应该使用-1代替。

from __future__ import print_function
import time
import gmpy2

def prime_numbers(limit=1000000):
    '''Prime number generator. Yields the series
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...
    using Sieve of Eratosthenes.
    '''
    sub_limit = int(limit**0.5)
    flags = gmpy2.xmpz(1)
    flags[(limit>>1)+1] = 1
    f_scan0 = flags.bit_scan0
    current = 0
    prime = 2
    while prime <= sub_limit:
        yield prime
        current += 1
        current = f_scan0(current)
        prime = 2 * current + 1
        flags[2*current*(current+1):limit>>1:prime] = -1
    while prime <= limit:
        yield prime
        current += 1
        current = f_scan0(current)
        prime = 2 * current + 1

start = time.time()
result = list(prime_numbers(1000000000))
print(time.time() - start)

编辑#5:我对gmpy2 2.0.0b2做了一些改进。您现在可以迭代所有已设置或清除的位。运行时间提高了约30%。

from __future__ import print_function
import time
import gmpy2

def sieve(limit=1000000):
    '''Returns a generator that yields the prime numbers up to limit.'''

    # Increment by 1 to account for the fact that slices do not include
    # the last index value but we do want to include the last value for
    # calculating a list of primes.
    sieve_limit = gmpy2.isqrt(limit) + 1
    limit += 1

    # Mark bit positions 0 and 1 as not prime.
    bitmap = gmpy2.xmpz(3)

    # Process 2 separately. This allows us to use p+p for the step size
    # when sieving the remaining primes.
    bitmap[4 : limit : 2] = -1

    # Sieve the remaining primes.
    for p in bitmap.iter_clear(3, sieve_limit):
        bitmap[p*p : limit : p+p] = -1

    return bitmap.iter_clear(2, limit)

if __name__ == "__main__":
    start = time.time()
    result = list(sieve(1000000000))
    print(time.time() - start)
    print(len(result))

答案 1 :(得分:8)

好的,所以这是我的第二个答案,但由于速度至关重要,我认为我必须提到bitarray模块 - 即使它是bitstring的克星:)。它非常适合这种情况,因为它不仅是一个C扩展(并且比纯Python更有希望),但它也支持切片分配。但它尚不适用于Python 3。

我甚至没有尝试优化它,我只是重写了bitstring版本。在我的机器上,我得到0.16秒的素数低于一百万。

十亿,它运行得非常好,并在2分31秒内完成。

import bitarray

def prime_bitarray(limit=1000000):
    yield 2
    flags = bitarray.bitarray(limit)
    flags.setall(False)
    sub_limit = int(limit**0.5)
    for i in xrange(3, limit, 2):
        if not flags[i]:
            yield i
            if i <= sub_limit:
                flags[3*i:limit:i*2] = True

答案 2 :(得分:8)

好的,这是一个(近乎完整的)全面的基准测试我今晚已经完成,看看哪个代码运行速度最快。希望有人会发现这个列表很有用。我省略了在我的机器上完成任何需要超过30秒的事情。

我要感谢所有投入的人。我从你的努力中获得了很多洞察力,我希望你也有。

我的机器:AMD ZM-86,2.40 Ghz双核,4GB内存。这是一台HP Touchsmart Tx2笔记本电脑。请注意,虽然我可能已链接到pastebin,但我在自己的计算机上对以下所有内容进行了基准测试。

一旦我能够构建它,我将添加gmpy2基准。

所有基准测试都在Python 2.6 x86中进行测试

  

返回最多1,000,000的素数列表:(使用 Python   发电机)

     

Sebastian's numpy generator version (updated) - 121 ms @

     

Mark's Sieve + Wheel - 154 ms

     

Robert's version with slicing - 159 ms

     

My improved version with slicing    - 205毫秒

     

Numpy generator with enumerate - 249 ms @

     

Mark's Basic Sieve - 317 ms

     

casevh's improvement on my original solution - 343 ms

     

My modified numpy generator solution - 407 ms

     

My original method in the question - 409 ms

     

Bitarray Solution - 414 ms @

     

Pure Python with bytearray - 1394 ms @

     

Scott's BitString solution - 6659   ms @

     

'@'表示该方法能够产生高达n

     

另外,如果你不需要的话   生成器,只想要整个列表   马上:

     

numpy solution from RosettaCode -   32毫秒@

     

(numpy解决方案也能够产生高达10亿,这需要61.6259秒。我怀疑内存被交换一次,因此是双倍时间。)

答案 3 :(得分:6)

相关问题:Fastest way to list all primes below N in python

嗨,我也在寻找Python中的代码来尽可能快地生成 10 ^ 9 的素数,这很难因为内存问题。到目前为止,我想出了这个来生成 10 ^ 6 &amp;的质数。 10 ^ 7 (在我的旧机器上分别计时0,171s和1,764s),这似乎比“我的改进版本切片”更快(至少在我的电脑中)< / em>来自之前的帖子,可能是因为我使用n//i-i +1(见下文)而不是其他代码中的类似len(flags[i2::i<<1])。如果我错了,请纠正我。任何改进建议都是非常受欢迎的。

def primes(n):
    """ Returns  a list of primes < n """
    sieve = [True] * n
    for i in xrange(3,int(n**0.5)+1,2):
        if sieve[i]:
            sieve[i*i::2*i]=[False]*((n-i*i-1)/(2*i)+1)
    return [2] + [i for i in xrange(3,n,2) if sieve[i]]

在他的一个代码中,Xavier使用flags[i2::i<<1]len(flags[i2::i<<1])。我明确地计算了大小,使用i*i..n之间我们(n-i*i)//2*i的{​​{1}}倍的事实,因为我们想要计算2*i,我们也总计i*i所以{ {1}}等于1。这使代码更快。基于上述代码的基本生成器将是:

len(sieve[i*i::2*i])

通过一些修改,可以编写一个稍慢版本的代码,以一半大小为(n-i*i)//(2*i) +1的筛子开始,并且适用于相同的def primesgen(n): """ Generates all primes <= n """ sieve = [True] * n yield 2 for i in xrange(3,int(n**0.5)+1,2): if sieve[i]: yield i sieve[i*i::2*i] = [False]*((n-i*i-1)/(2*i)+1) for i in xrange(i+2,n,2): if sieve[i]: yield i 。不确定这将如何反映在内存问题上。作为一个实现的例子,这里是 numpy rosetta代码的修改版本(可能更快)从一半大小的筛子开始。

sieve = [True] * (n//2)

更快更好更多内存生成器将是:

n

或者使用更多代码:

import numpy
def primesfrom3to(n):
    """ Returns a array of primes, 3 <= p < n """
    sieve = numpy.ones(n/2, dtype=numpy.bool)
    for i in xrange(3,int(n**0.5)+1,2):
        if sieve[i/2]: sieve[i*i/2::i] = False
    return 2*numpy.nonzero(sieve)[0][1::]+1

Ps:如果您对如何加快此代码提出任何建议,从更改操作顺序到预先计算任何内容,请发表评论。

答案 4 :(得分:4)

这是我之前写的一个版本;与你的速度进行比较可能会很有趣。它对空间问题没有任何作用(事实上,它们可能比你的版本更糟)。

from math import sqrt

def basicSieve(n):
    """Given a positive integer n, generate the primes < n."""
    s = [1]*n
    for p in xrange(2, 1+int(sqrt(n-1))):
        if s[p]:
            a = p*p
            s[a::p] = [0] * -((a-n)//p)
    for p in xrange(2, n):
        if s[p]:
            yield p 

我有更快的版本,使用滚轮,但它们要复杂得多。原始来源为here

好的,这是使用滚轮的版本。 wheelSieve是主要的切入点。

from math import sqrt
from bisect import bisect_left

def basicSieve(n):
    """Given a positive integer n, generate the primes < n."""
    s = [1]*n
    for p in xrange(2, 1+int(sqrt(n-1))):
        if s[p]:
            a = p*p
            s[a::p] = [0] * -((a-n)//p)
    for p in xrange(2, n):
        if s[p]:
            yield p

class Wheel(object):
    """Class representing a wheel.

    Attributes:
       primelimit -> wheel covers primes < primelimit.
       For example, given a primelimit of 6
       the wheel primes are 2, 3, and 5.
       primes -> list of primes less than primelimit
       size -> product of the primes in primes;  the modulus of the wheel
       units -> list of units modulo size
       phi -> number of units

    """
    def __init__(self, primelimit):
        self.primelimit = primelimit
        self.primes = list(basicSieve(primelimit))

        # compute the size of the wheel
        size = 1
        for p in self.primes:
            size *= p
        self.size = size

        # find the units by sieving
        units = [1] * self.size
        for p in self.primes:
            units[::p] = [0]*(self.size//p)
        self.units = [i for i in xrange(self.size) if units[i]]

        # number of units
        self.phi = len(self.units)

    def to_index(self, n):
        """Compute alpha(n), where alpha is an order preserving map
        from the set of units modulo self.size to the nonnegative integers.

        If n is not a unit, the index of the first unit greater than n
        is given."""
        return bisect_left(self.units, n % self.size) + n // self.size * self.phi

    def from_index(self, i):
        """Inverse of to_index."""

        return self.units[i % self.phi] + i // self.phi * self.size

def wheelSieveInner(n, wheel):
    """Given a positive integer n and a wheel, find the wheel indices of
    all primes that are less than n, and that are units modulo the
    wheel modulus.
    """

    # renaming to avoid the overhead of attribute lookups
    U = wheel.units
    wS = wheel.size
    # inverse of unit map
    UI = dict((u, i) for i, u in enumerate(U))
    nU = len(wheel.units)

    sqroot = 1+int(sqrt(n-1)) # ceiling of square root of n

    # corresponding index (index of next unit up)
    sqrti = bisect_left(U, sqroot % wS) + sqroot//wS*nU
    upper = bisect_left(U, n % wS) + n//wS*nU
    ind2 = bisect_left(U, 2 % wS) + 2//wS*nU

    s = [1]*upper
    for i in xrange(ind2, sqrti):
        if s[i]:
            q = i//nU
            u = U[i%nU]
            p = q*wS+u
            u2 = u*u
            aq, au = (p+u)*q+u2//wS, u2%wS
            wp = p * nU
            for v in U:
                # eliminate entries corresponding to integers
                # congruent to p*v modulo p*wS
                uvr = u*v%wS
                m = aq + (au > uvr)
                bot = (m + (q*v + u*v//wS - m) % p) * nU + UI[uvr]
                s[bot::wp] = [0]*-((bot-upper)//wp)
    return s

def wheelSieve(n, wheel=Wheel(10)):
    """Given a positive integer n, generate the list of primes <= n."""
    n += 1
    wS = wheel.size
    U = wheel.units
    nU = len(wheel.units)
    s = wheelSieveInner(n, wheel)
    return (wheel.primes[:bisect_left(wheel.primes, n)] +
            [p//nU*wS + U[p%nU] for p in xrange(bisect_left(U, 2 % wS)
             + 2//wS*nU, len(s)) if s[p]])

关于什么是轮子:嗯,你知道(除了2),所有素数都是奇数,所以大多数筛子错过了所有偶数。类似地,您可以进一步注意所有质数(除了2和3)与1或5模6(== 2 * 3)一致,因此您只能在筛子中存储这些数字的条目。下一步是要注意所有素数(2,3和5除外)与1,7,11,13,17,19,23,29(模30)中的一个一致(这里30 == 2 * 3 * 5),等等。

答案 5 :(得分:3)

使用bitstring可以提高速度是在排除当前数字的倍数时使用'set'方法。

所以重要的部分变成

for i in range(3, limit, 2):
    if flags[i]:
        yield i
        if i <= sub_limit:
            flags.set(1, range(i*3, limit, i*2))    

在我的机器上,它比原来的速度快3倍。

我的另一个想法是使用bitstring来表示奇数。然后,您可以使用返回生成器的flags.findall([0])找到未设置的位。不确定这是否会更快(找到位模式并不是非常容易有效)。

[完全披露:我写了bitstring模块,所以我在这里有一些自豪感!]


作为比较,我还从bittring方法中获取了内容,以便它以相同的方式执行,但是没有范围检查等。我认为这为纯粹的Python提供了合理的下限元素(不改变算法,我认为这是作弊!)

def prime_pure(limit=1000000):
    yield 2
    flags = bytearray((limit + 7) // 8)
    sub_limit = int(limit**0.5)
    for i in xrange(3, limit, 2):
        byte, bit = divmod(i, 8)
        if not flags[byte] & (128 >> bit):
            yield i
            if i <= sub_limit:
                for j in xrange(i*3, limit, i*2):
                    byte, bit = divmod(j, 8)
                    flags[byte] |= (128 >> bit)

在我的机器上,对于一百万个元素,它在大约0.62秒内运行,这意味着它大约是比特阵列答案速度的四分之一。我认为纯Python非常合理。

答案 6 :(得分:2)

Python的整数类型可以是任意大小,因此您不需要一个聪明的位串库来表示一组位,只需一个数字。

这是代码。它可以轻松处理1,000,000的限制,甚至可以处理10,000,000而不会抱怨太多:

def multiples_of(n, step, limit):
    bits = 1 << n
    old_bits = bits
    max = 1 << limit
    while old_bits < max:
        old_bits = bits
        bits += bits << step
        step *= 2
    return old_bits

def prime_numbers(limit=10000000):
    '''Prime number generator. Yields the series                                                                        
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29 ...                                                                              
    using Sieve of Eratosthenes.                                                                                        
    '''
    yield 2
    sub_limit = int(limit**0.5)
    flags = ((1 << (limit - 2)) - 1) << 2
    # Step through all the odd numbers                                                                                  
    for i in xrange(3, limit, 2):
        if not (flags & (1 << i)):
            continue
        yield i
        # Exclude further multiples of the current prime number                                                         
        if i <= sub_limit:
            flags &= ~multiples_of(i * 3, i << 1, limit)

multiples_of功能可以避免单独设置每个单独的费用。它设置初始位,将其移位初始步(i << 1)并将结果添加到自身。然后它将步骤加倍,以便下一个移位复制两个位,依此类推,直到达到极限。这会将数字的所有倍数设置为O(log(限制))时间内的限制。

答案 7 :(得分:2)

您可能想要查看的一个选项是编译C / C ++模块,这样您就可以直接访问比特错误的功能。 AFAIK你可以编写一些这种性质的东西,只有在完成C / C ++中执行的计算时才会返回结果。现在我正在键入这个你也可以看看numpy,它将数组存储为本机C的速度。我不知道是否会比bitstring模块更快!

答案 8 :(得分:1)

另一个非常愚蠢的选择,但如果你真的需要一个非常快的可用数量的大量素数,这可能会有所帮助。比如说,如果你需要它们作为回答项目Euler问题的工具(如果问题只是优化你的代码作为心智游戏那么无关紧要)。

使用任何慢速解决方案生成列表并将其保存到python源文件,primes.py只包含:

primes = [ a list of a million primes numbers here ]

现在要使用它们,您只需要执行import primes,并以磁盘IO的速度获得最小的内存占用。复杂性是素数: - )

即使您使用了经过优化的解决方案来生成此列表,它也只会执行一次并且无关紧要。

使用pickle / unpickle你可能会更快,但你明白了......

答案 9 :(得分:1)

你可以使用分段的Eratosthenes筛子。用于每个段的内存将重新用于下一个段。

答案 10 :(得分:1)

以下是一些Python3代码,它使用的内存少于迄今发布的bitarray / bitstring解决方案,大约是Robert William Hanks的primesgen()内存的1/8,而运行速度比primesgen()快得多(略高于1,000,000,使用37KB的内存,比使用34MB的1,000,000,000的primesgen()快3倍。增加块大小(代码中的变量块)使用更多内存但加速程序,达到极限 - 我选择了一个值,使其对内存的贡献低于筛子的n // 30字节的10%。它不像Will Ness's infinite generator那样具有内存效率(另请参阅https://stackoverflow.com/a/19391111/5439078),因为它记录(并以压缩形式返回结尾)所有计算的素数。

只要平方根计算准确(如果Python使用64位双精度,大约2 ** 51),这应该可以正常工作。但是你不应该使用这个程序来找到那么大的素数!

(我没有重新计算偏移量,只是从Robert William Hanks的代码中取出它们。感谢Robert!)

import numpy as np
def prime_30_gen(n):
    """ Input n, int-like.  Yields all primes < n as Python ints"""
    wheel = np.array([2,3,5])
    yield from wheel[wheel < n].tolist()
    powers = 1 << np.arange(8, dtype='u1')
    odds = np.array([1, 7, 11, 13, 17, 19, 23, 29], dtype='i8')
    offsets=np.array([[0,6,10,12,16,18,22,28],[6,24,16,12,4,0,22,10],
                      [0,6,20,12,26,18,2,8],  [24,6,4,18,16,0,28,10],
                      [6,24,26,12,14,0,2,20], [0,24,10,18,4,12,28,22],
                      [24,6,14,18,26,0,8,20], [0,24,20,18,14,12,8,2]],
                     dtype='i8')
    offsets = {d:f for d,f in zip(odds, offsets)}
    sieve = 255 * np.ones((n + 28) // 30, dtype='u1')
    if n % 30 > 1: sieve[-1] >>= 8 - odds.searchsorted(n % 30)
    sieve[0] &= ~1 
    for i in range((int((n - 1)**0.5) + 29) // 30):
        for odd in odds[(sieve[i] & powers).nonzero()[0]]:
            k = i * 30 + odd
            yield int(k)
            for clear_bit, off in zip(~powers, offsets[odd]): 
                sieve[(k * (k + off)) // 30 :: k] &= clear_bit
    chunk = min(1 + (n >> 13), 1<<15)
    for i in range(i + 1, len(sieve), chunk):
        a = (sieve[i : i + chunk, np.newaxis] & powers)
        a = np.flatnonzero(a.astype('bool')) + i * 8
        a = (a // 8 * 30 + odds[a & 7]).tolist()
        yield from a
    return sieve

旁注:如果你想要真正的速度并且要求素数列表需要2GB的RAM到10 ** 9,那么你应该使用pyprimesieve(在https://pypi.python.org/上,使用primesieve http://primesieve.org/