Porting optimized Sieve of Eratosthenes from Python to C++

前端 未结 3 964
佛祖请我去吃肉
佛祖请我去吃肉 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).

提交回复
热议问题