How to implement an efficient infinite generator of prime numbers in Python?

后端 未结 13 2423
醉酒成梦
醉酒成梦 2020-11-22 01:50

This is not a homework, I am just curious.

INFINITE is the key word here.

I wish to use it as for p in primes(). I believe that this is a built-

13条回答
  •  谎友^
    谎友^ (楼主)
    2020-11-22 02:36

    I know the post is old, but I came by myself across this question... The following code is based on a very simple idea: a growing sieve of Eratosthenes. This solution is really slower than the best ones here, but it is easy to grasp and designed to be readable...

    I used integers to store the results of the sieve. In binary format, an integer is a list of 0s and 1s, 0 at position i if i is not a prime, 1 if it may be a prime. The requisite infinity is a result of the fact that Python 3 integers are unbounded.

    def primes():
        container, size = 1 << 2, 3 # we start with 0b100 (from right to left: 0 and 1 are not primes, 2 is
        last_prime = 1
        while True:
            prime = next((j for j in range(last_prime+1, size) if container & 1 << j), None) # find the next prime
            while not prime:
                container, size = expand(container, size, 2**16) # add 65536 cells and sieve the container
                prime = next((j for j in range(last_prime+1, size) if container & 1 << j), None)
            yield prime
        last_prime = prime
    

    How to expand the container? Just add a bunch of 1s at the left of the container (in binary format) and sieve them. This is identical to the standard sieve, with a slight difference. In the standard sieve, if we find a prime i, we start to cross the cells at i*i, with a step of i.

    Here, this may have been done for the first part of container. We just need to start at the beginnig of the new part of the container if it is farther than i*i.

    def expand(container, size, n):
        new_size = size + n
        container += (1 << (new_size + 1) - 1) - (1 << size) # add n 1's
        for i in range(2, new_size):
            if container & (1 << i): # i is a prime
                t = sum(1 << j for j in range(max(i, size // i)*i, new_size, i)) # set 1 for all mutiple
                container &= ~t # cross the cells
    
        return container, new_size
    

    Test for a million primes:

    import itertools
    assert 78498 == len(list(itertools.takewhile(lambda p: p<1000000, primes())))
    

提交回复
热议问题