Sieve of Eratosthenes - Finding Primes Python

前端 未结 17 2315
旧巷少年郎
旧巷少年郎 2020-11-22 04:40

Just to clarify, this is not a homework problem :)

I wanted to find primes for a math application I am building & came across Sieve of Eratosthenes approach.

相关标签:
17条回答
  • 2020-11-22 05:02

    I realise this isn't really answering the question of how to generate primes quickly, but perhaps some will find this alternative interesting: because python provides lazy evaluation via generators, eratosthenes' sieve can be implemented exactly as stated:

    def intsfrom(n):
        while True:
            yield n
            n += 1
    
    def sieve(ilist):
        p = next(ilist)
        yield p
        for q in sieve(n for n in ilist if n%p != 0):
            yield q
    
    
    try:
        for p in sieve(intsfrom(2)):
            print p,
    
        print ''
    except RuntimeError as e:
        print e
    

    The try block is there because the algorithm runs until it blows the stack and without the try block the backtrace is displayed pushing the actual output you want to see off screen.

    0 讨论(0)
  • 2020-11-22 05:03

    I prefer NumPy because of speed.

    import numpy as np
    
    # Find all prime numbers using Sieve of Eratosthenes
    def get_primes1(n):
        m = int(np.sqrt(n))
        is_prime = np.ones(n, dtype=bool)
        is_prime[:2] = False  # 0 and 1 are not primes
    
        for i in range(2, m):
            if is_prime[i] == False:
                continue
            is_prime[i*i::i] = False
    
        return np.nonzero(is_prime)[0]
    
    # Find all prime numbers using brute-force.
    def isprime(n):
        ''' Check if integer n is a prime '''
        n = abs(int(n))  # n is a positive integer
        if n < 2:  # 0 and 1 are not primes
            return False
        if n == 2:  # 2 is the only even prime number
            return True
        if not n & 1:  # all other even numbers are not primes
            return False
        # Range starts with 3 and only needs to go up the square root
        # of n for all odd numbers
        for x in range(3, int(n**0.5)+1, 2):
            if n % x == 0:
                return False
        return True
    
    # To apply a function to a numpy array, one have to vectorize the function
    def get_primes2(n):
        vectorized_isprime = np.vectorize(isprime)
        a = np.arange(n)
        return a[vectorized_isprime(a)]
    

    Check the output:

    n = 100
    print(get_primes1(n))
    print(get_primes2(n))    
        [ 2  3  5  7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97]
        [ 2  3  5  7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97]
    

    Compare the speed of Sieve of Eratosthenes and brute-force on Jupyter Notebook. Sieve of Eratosthenes in 539 times faster than brute-force for million elements.

    %timeit get_primes1(1000000)
    %timeit get_primes2(1000000)
    4.79 ms ± 90.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
    2.58 s ± 31.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
    
    0 讨论(0)
  • You're not quite implementing the correct algorithm:

    In your first example, primes_sieve doesn't maintain a list of primality flags to strike/unset (as in the algorithm), but instead resizes a list of integers continuously, which is very expensive: removing an item from a list requires shifting all subsequent items down by one.

    In the second example, primes_sieve1 maintains a dictionary of primality flags, which is a step in the right direction, but it iterates over the dictionary in undefined order, and redundantly strikes out factors of factors (instead of only factors of primes, as in the algorithm). You could fix this by sorting the keys, and skipping non-primes (which already makes it an order of magnitude faster), but it's still much more efficient to just use a list directly.

    The correct algorithm (with a list instead of a dictionary) looks something like:

    def primes_sieve2(limit):
        a = [True] * limit                          # Initialize the primality list
        a[0] = a[1] = False
    
        for (i, isprime) in enumerate(a):
            if isprime:
                yield i
                for n in range(i*i, limit, i):     # Mark factors non-prime
                    a[n] = False
    

    (Note that this also includes the algorithmic optimization of starting the non-prime marking at the prime's square (i*i) instead of its double.)

    0 讨论(0)
  • 2020-11-22 05:06

    Removing from the beginning of an array (list) requires moving all of the items after it down. That means that removing every element from a list in this way starting from the front is an O(n^2) operation.

    You can do this much more efficiently with sets:

    def primes_sieve(limit):
        limitn = limit+1
        not_prime = set()
        primes = []
    
        for i in range(2, limitn):
            if i in not_prime:
                continue
    
            for f in range(i*2, limitn, i):
                not_prime.add(f)
    
            primes.append(i)
    
        return primes
    
    print primes_sieve(1000000)
    

    ... or alternatively, avoid having to rearrange the list:

    def primes_sieve(limit):
        limitn = limit+1
        not_prime = [False] * limitn
        primes = []
    
        for i in range(2, limitn):
            if not_prime[i]:
                continue
            for f in xrange(i*2, limitn, i):
                not_prime[f] = True
    
            primes.append(i)
    
        return primes
    
    0 讨论(0)
  • 2020-11-22 05:06

    I just came up with this. It may not be the fastest, but I'm not using anything other than straight additions and comparisons. Of course, what stops you here is the recursion limit.

    def nondivsby2():
        j = 1
        while True:
            j += 2
            yield j
    
    def nondivsbyk(k, nondivs):
        j = 0
        for i in nondivs:
            while j < i:
                j += k
            if j > i:
                yield i
    
    def primes():
        nd = nondivsby2()
        while True:
            p = next(nd)
            nd = nondivsbyk(p, nd)
            yield p
    
    def main():
        for p in primes():
            print(p)
    
    0 讨论(0)
  • 2020-11-22 05:07

    The fastest implementation I could come up with:

    isprime = [True]*N
    isprime[0] = isprime[1] = False
    for i in range(4, N, 2):
        isprime[i] = False
    for i in range(3, N, 2):
        if isprime[i]:
            for j in range(i*i, N, 2*i):
                isprime[j] = False
    
    0 讨论(0)
提交回复
热议问题