Porting optimized Sieve of Eratosthenes from Python to C++

前端 未结 3 958
佛祖请我去吃肉
佛祖请我去吃肉 2021-01-03 12:33

Some time ago I used the (blazing fast) primesieve in python that I found here: Fastest way to list all primes below N

To be precise, this implementation:

         


        
相关标签:
3条回答
  • 2021-01-03 12:57

    I'll try to explain as much as I can. The sieve array has an unusual indexing scheme; it stores a bit for each number that is congruent to 1 or 5 mod 6. Thus, a number 6*k + 1 will be stored in position 2*k and k*6 + 5 will be stored in position 2*k + 1. The 3*i+1|1 operation is the inverse of that: it takes numbers of the form 2*n and converts them into 6*n + 1, and takes 2*n + 1 and converts it into 6*n + 5 (the +1|1 thing converts 0 to 1 and 3 to 5). The main loop iterates k through all numbers with that property, starting with 5 (when i is 1); i is the corresponding index into sieve for the number k. The first slice update to sieve then clears all bits in the sieve with indexes of the form k*k/3 + 2*m*k (for m a natural number); the corresponding numbers for those indexes start at k^2 and increase by 6*k at each step. The second slice update starts at index k*(k-2*(i&1)+4)/3 (number k * (k+4) for k congruent to 1 mod 6 and k * (k+2) otherwise) and similarly increases the number by 6*k at each step.

    Here's another attempt at an explanation: let candidates be the set of all numbers that are at least 5 and are congruent to either 1 or 5 mod 6. If you multiply two elements in that set, you get another element in the set. Let succ(k) for some k in candidates be the next element (in numerical order) in candidates that is larger than k. In that case, the inner loop of the sieve is basically (using normal indexing for sieve):

    for k in candidates:
      for (l = k; ; l += 6) sieve[k * l] = False
      for (l = succ(k); ; l += 6) sieve[k * l] = False
    

    Because of the limitations on which elements are stored in sieve, that is the same as:

    for k in candidates:
      for l in candidates where l >= k:
        sieve[k * l] = False
    

    which will remove all multiples of k in candidates (other than k itself) from the sieve at some point (either when the current k was used as l earlier or when it is used as k now).

    0 讨论(0)
  • 2021-01-03 13:05

    As an aside, you can "approximate" prime numbers. Call the approximate prime P. Here are a few formulas:

    P = 2*k+1 // not divisible by 2

    P = 6*k + {1, 5} // not divisible 2, 3

    P = 30*k + {1, 7, 11, 13, 17, 19, 23, 29} // not divisble by 2, 3, 5

    The properties of the set of numbers found by these formulas is that P may not be prime, however all primes are in the set P. I.e. if you only test numbers in the set P for prime, you won't miss any.

    You can reformulate these formulas to:

    P = X*k + {-i, -j, -k, k, j, i}

    if that is more convenient for you.

    Here is some code that uses this technique with a formula for P not divisible by 2, 3, 5, 7.

    This link may represent the extent to which this technique can be practically leveraged.

    0 讨论(0)
  • 2021-01-03 13:16

    Piggy-Backing onto Howard Hinnant's response, Howard, you don't have to test numbers in the set of all natural numbers not divisible by 2, 3 or 5 for primality, per se. You need simply multiply each number in the array (except 1, which self-eliminates) times itself and every subsequent number in the array. These overlapping products will give you all the non-primes in the array up to whatever point you extend the deterministic-multiplicative process. Thus the first non-prime in the array will be 7 squared, or 49. The 2nd, 7 times 11, or 77, etc. A full explanation here: http://www.primesdemystified.com

    0 讨论(0)
提交回复
热议问题