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

后端 未结 13 2483
醉酒成梦
醉酒成梦 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条回答
  •  猫巷女王i
    2020-11-22 02:31

    Here's a pretty fast infinite generator, written in Python2 but easily adjusted to Python3. To use it to add the primes up to 10**9, use the following:

    from itertools import takewhile
    from functools import partial
    from operator import gt
    print (sum(takewhile(partial(gt, 10**9), prime_gen_inf())))
    

    It's a segmented sieve, faster but obviously less elegant than Will Ness's algorithm.

    from operator import mul
    from functools import reduce
    def prod(x): return reduce(mul, x, 1)
    
    
    def build_sieve(wheel):
        w = prod(wheel)
        w_phi = prod([p-1 for p in wheel])
        rems = [a for a in range(w) if all(a % p for p in wheel)]
        assert len(rems) == w_phi
        inv = {a:pow(a, w_phi - 1, w) for a in rems}
        try:
            known_p = wheel + rems[1 : rems.index(rems[1]*rems[1])]
        except ValueError:
            known_p = wheel + rems[1:]
        return wheel, w, w_phi, rems, inv, known_p
    
    #Adjust the chunk variable based on your computer's architecture.
    #
    #Adjust the line with #! if you don't need "true" infinite.  If you don't need
    #primes larger than 1<<32, use array('H', []), if 1<<64 use 'L', if 1<<128 (in
    #Python3) use 'Q', otherwise use empty list [].
    #To save memory, comment out the lines with #*, and uncomment the commented-out
    #lines 
    import itertools
    from itertools import islice, count, compress, izip
    chain_f = itertools.chain.from_iterable
    from array import array
    def prime_gen_inf(chunk=250000, sieve_info = build_sieve([2,3,5,7])):
        """    Indefinitely yields primes    """
        wheel, w, w_phi, rems, inv, known_p = sieve_info
        for p in known_p: yield p
        new_n = 0;
        while True:
            size = min(chunk, (p * p - new_n) / w)
            sieve = bytearray([1]) * size * w_phi
            n, new_n = new_n, new_n + size * w
            if not n:
                zero = bytearray([0])
                seen = len(known_p) - len(wheel) + 1
                sieve[:seen:1] = zero * seen
                p_gen = islice(prime_gen_inf(), len(wheel), None)
                new_p = next(p_gen)
                ps = []                                         #! array('H', [])
                p_invs = bytearray([])                                         #*
            while new_p * new_p < new_n:
                ps.append(new_p)
                p_invs.append(inv[new_p % w])                                  #*
                new_p = next(p_gen)
            for p, p_inv, modp in izip(ps, p_invs, [-n % p for p in ps]):      #*
                s = [(modp + p * (p_inv * (r - modp) % w)) / w for r in rems]  #*
            #for p in ps:
            #    s = [(-n%p + p * (inv[p%w] * (r - -n%p) % w)) / w for r in rems]
                for i, start in enumerate(s):
                    slice_size = ((size - start - 1) / p + 1)
                    sieve[i + start * w_phi :: p * w_phi] = zero * slice_size
            for p in compress(chain_f(izip(*[count(n+r, w) for r in rems])), sieve):
                yield p
    

提交回复
热议问题