Sieve of Eratosthenes - Finding Primes Python

前端 未结 17 2314
旧巷少年郎
旧巷少年郎 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:08

    By combining contributions from many enthusiasts (including Glenn Maynard and MrHIDEn from above comments), I came up with following piece of code in python 2:

    def simpleSieve(sieveSize):
        #creating Sieve.
        sieve = [True] * (sieveSize+1)
        # 0 and 1 are not considered prime.
        sieve[0] = False
        sieve[1] = False
        for i in xrange(2,int(math.sqrt(sieveSize))+1):
            if sieve[i] == False:
                continue
            for pointer in xrange(i**2, sieveSize+1, i):
                sieve[pointer] = False
        # Sieve is left with prime numbers == True
        primes = []
        for i in xrange(sieveSize+1):
            if sieve[i] == True:
                primes.append(i)
        return primes
    
    sieveSize = input()
    primes = simpleSieve(sieveSize)
    

    Time taken for computation on my machine for different inputs in power of 10 is:

    • 3 : 0.3 ms
    • 4 : 2.4 ms
    • 5 : 23 ms
    • 6 : 0.26 s
    • 7 : 3.1 s
    • 8 : 33 s
    0 讨论(0)
  • 2020-11-22 05:11

    Using a bit of numpy, I could find all primes below 100 million in a little over 2 seconds.

    There are two key features one should note

    • Cut out multiples of i only for i up to root of n
    • Setting multiples of i to False using x[2*i::i] = False is much faster than an explicit python for loop.

    These two significantly speed up your code. For limits below one million, there is no perceptible running time.

    import numpy as np
    
    def primes(n):
        x = np.ones((n+1,), dtype=np.bool)
        x[0] = False
        x[1] = False
        for i in range(2, int(n**0.5)+1):
            if x[i]:
                x[2*i::i] = False
    
        primes = np.where(x == True)[0]
        return primes
    
    print(len(primes(100_000_000)))
    
    0 讨论(0)
  • 2020-11-22 05:13

    Here's a version that's a bit more memory-efficient (and: a proper sieve, not trial divisions). Basically, instead of keeping an array of all the numbers, and crossing out those that aren't prime, this keeps an array of counters - one for each prime it's discovered - and leap-frogging them ahead of the putative prime. That way, it uses storage proportional to the number of primes, not up to to the highest prime.

    import itertools
    
    def primes():
    
        class counter:
            def __init__ (this,  n): this.n, this.current,  this.isVirgin = n, n*n,  True
                # isVirgin means it's never been incremented
            def advancePast (this,  n): # return true if the counter advanced
                if this.current > n:
                    if this.isVirgin: raise StopIteration # if this is virgin, then so will be all the subsequent counters.  Don't need to iterate further.
                    return False
                this.current += this.n # pre: this.current == n; post: this.current > n.
                this.isVirgin = False # when it's gone, it's gone
                return True
    
        yield 1
        multiples = []
        for n in itertools.count(2):
            isPrime = True
            for p in (m.advancePast(n) for m in multiples):
                if p: isPrime = False
            if isPrime:
                yield n
                multiples.append (counter (n))
    

    You'll note that primes() is a generator, so you can keep the results in a list or you can use them directly. Here's the first n primes:

    import itertools
    
    for k in itertools.islice (primes(),  n):
        print (k)
    

    And, for completeness, here's a timer to measure the performance:

    import time
    
    def timer ():
        t,  k = time.process_time(),  10
        for p in primes():
            if p>k:
                print (time.process_time()-t,  " to ",  p,  "\n")
                k *= 10
                if k>100000: return
    

    Just in case you're wondering, I also wrote primes() as a simple iterator (using __iter__ and __next__), and it ran at almost the same speed. Surprised me too!

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

    A simple speed hack: when you define the variable "primes," set the step to 2 to skip all even numbers automatically, and set the starting point to 1.

    Then you can further optimize by instead of for i in primes, use for i in primes[:round(len(primes) ** 0.5)]. That will dramatically increase performance. In addition, you can eliminate numbers ending with 5 to further increase speed.

    0 讨论(0)
  • 2020-11-22 05:22
    import math
    def sieve(n):
        primes = [True]*n
        primes[0] = False
        primes[1] = False
        for i in range(2,int(math.sqrt(n))+1):
                j = i*i
                while j < n:
                        primes[j] = False
                        j = j+i
        return [x for x in range(n) if primes[x] == True]
    
    0 讨论(0)
提交回复
热议问题