带轮分解的Eratosthenes筛

时间:2013-07-26 22:57:52

标签: c++ algorithm primes sieve-of-eratosthenes wheel-factorization

我正在实施一个相当快速的素数发生器,我通过对eratosthenes筛子的一些优化获得了一些不错的结果。特别是,在算法的初步部分,我以这种方式跳过所有2和3的倍数:

template<class Sieve, class SizeT>
void PrimeGenerator<Sieve, SizeT>::factorize()
{
    SizeT c = 2;
    m_sieve[2] = 1;
    m_sieve[3] = 1;

    for (SizeT i=5; i<m_size; i += c, c = 6 - c)
        m_sieve[i] = 1;
}

根据eratosthenes的筛子,m_sieve是一个布尔数组。 我认为这是一种只考虑素数2和3的车轮分解,按照模式2,4,2,4进行递增。我想要做的是实现更大的轮子,可能考虑质数2,3和5。 我已经阅读了很多关于它的文档,但是我没有看到任何关于eratosthenes筛选的实现...示例代码可以帮助很多,但也有一些提示会很好:) 感谢。

4 个答案:

答案 0 :(得分:3)

你可以走得更远。这是我几年前写的一些OCaml代码:

let eratosthene borne =
  let remove_multiples a lst =
    let rec remmult multa li accu = function
        []         -> rev accu
      | head::tail ->
          if multa = head
          then remmult (a*(hd li)) (tl li)  accu      tail
          else remmult   multa        li (head::accu) tail
    in
    remmult (a * a) lst [] lst
  in
  let rec first_primes accu ll =
    let a = hd ll in 
    if a * a > borne then (rev accu) @ ll 
    else first_primes (a::accu) (remove_multiples a (tl ll))
  in
  let start_list =
(* Hard code of the differences of consecutive numbers that are prime*)
(* with 2 3 5 7 starting with 11... *) 
    let rec lrec = 2 :: 4 :: 2 :: 4 :: 6 :: 2 :: 6 :: 4 :: 2 :: 4 :: 6
      :: 6 :: 2 :: 6 :: 4 :: 2 :: 6 :: 4 :: 6 :: 8 :: 4 :: 2 :: 4 :: 2
      :: 4 :: 8 :: 6 :: 4 :: 6 :: 2 :: 4 :: 6 :: 2 :: 6 :: 6 :: 4 :: 2
      :: 4 :: 6 :: 2 :: 6 :: 4 :: 2 :: 4 :: 2 :: 10 :: 2 :: 10 :: lrec 
    and listPrime2357 a llrec accu =
      if a > borne then rev accu
      else listPrime2357 (a + (num (hd llrec))) (tl llrec) (a::accu)
    in
    listPrime2357 (num 11) lrec []
  in
  first_primes [(num 7);(num 5);(num 3);(num 2)] start_list;;

注意OCaml允许循环链表的好技巧。

答案 1 :(得分:2)

为IBM工作的澳大利亚数学家Paul Pritchard在20世纪80年代开发了一系列轮式筛子。我在my blog讨论其中一个,包括手工制作的示例和Scheme中的实现。这里讨论太大了。您应该意识到,虽然渐近复杂度小于Eratosthenes的Sieve,但实现细节通常会使其在实践中变慢。

答案 2 :(得分:2)

2 * 3 * 5 = 30
辐条= 2,3,4,5,6,8,9,10,12,15,16,18,20,24,30
辐条之间的数字:1,7,11,13,17,19,23,25,29

int[] gaps = [6,4,2,4,2,4,2,4];
int[] primes = [2,3,5];
int max = 9001;
int counter, max_visited;
while(max_visited < max) {
  int jump = gaps[counter];
  counter = counter + 1 % gaps.length;
  max_visited += jump;
}

这有帮助吗?

或者,这可能是你想要的,伪代码:

primes = [2,3,5];
product = multiply(primes);//imaginary function that returns 30
wheel = new int[product];
foreach(prime in primes)
  for(int x = 1; x <= product/prime; x++)
    wheel[prime*x] = 1;
return wheel

答案 3 :(得分:0)

它有一个更好的优化(它需要O(n)操作而不是O(n log log n)为你的变体):https://stackoverflow.com/a/17550147/2559094,但它需要更多的内存(使用n + n / log n记忆而不是n)。

如果您仍想继续进行优化并考虑质数p1,p2,...,pn,则应将所有数字从0写入p1 * p2 * ... * pn(如果您决定使用lcm不仅使用素数)并检查其中哪些满足以下系统: x!= 0(mod p1) x!= 0(mod p2) ... x!= 0(mod pn)

然后你应该找到这些数字之间的所有间隙,并制作一个这些间隙的数组来使用它们。