代码之家  ›  专栏  ›  技术社区  ›  marc lincoln

阿特金解释筛

  •  21
  • marc lincoln  · 技术社区  · 16 年前

    我现在正在做一个项目,我需要一个有效的方法来计算素数。我已经用过了 sieve of Eratosthenes 但是,我一直在四处寻找,发现 sieve of Atkin 是一种更有效的方法。我发现很难找到一个解释(我已经能够理解!)这种方法。它是如何工作的?示例代码(最好使用C或Python)将非常出色。

    编辑: 感谢您的帮助,我唯一还不明白的是x和y变量在伪代码中所指的是什么。有人能帮我解释一下这个吗?

    5 回复  |  直到 6 年前
        1
  •  13
  •   Noldorin    9 年前

    这个 wiki page 始终是一个很好的起点,因为它完整地解释了算法并提供了带注释的伪代码。(注意,这里有很多细节,而且由于wiki网站是可靠的,所以我不会在这里引用。)

    对于您提到的特定语言的参考:

    希望有帮助。

        2
  •  10
  •   Will Ness Derri Leahy    6 年前

    Wikipedia article 有一个解释:

    • “算法完全忽略任何可以被2、3或5整除的数字。所有具有偶数模六十余数的数都可以被二除,而不是质数。所有模六十余数可被三除的数也可被三除,而不是质数。所有模六十余数可被五除的数都可被五除,而不是质数。所有这些剩余物都被忽略了。”

    让我们从著名的

    primes = sieve [2..] = sieve (2:[3..])   
      -- primes are sieve of list of 2,3,4... , i.e. 2 prepended to 3,4,5...
    sieve (x:xs) = x : sieve [y | y <- xs, rem y x /= 0]   -- set notation
      -- sieve of list of (x prepended to xs) is x prepended to the sieve of 
      --                  list of `y`s where y is drawn from xs and y % x /= 0
    

    让我们看看它是如何进行的,首先要做的几个步骤是:

    primes = sieve [2..] = sieve (2:[3..]) 
                         = 2 : sieve p2     -- list starting w/ 2, the rest is (sieve p2)
    p2 = [y | y <- [3..], rem y 2 /= 0]     -- for y from 3 step 1: if y%2 /= 0: yield y
    

    p2 不包含的倍数 . Low它只包含与 -没有号码在 P2 作为它的因素。寻找 P2 我们实际上不需要测试除以 每一个数字 [3..] (那是 然后, 3,4,5,6,7,… ,因为我们可以枚举 预先:

    rem y 2 /= 0  ===  not (ordElem y [2,4..])     -- "y is not one of 2,4,6,8,10,..."
    

    ordElem 就像 elem (即 IS元素 测试) 假设 它的list参数是一个有序的、不断增加的数字列表,因此它可以安全地检测不存在和存在:

    ordElem y xs = take 1 (dropWhile (< y) xs) == [y]   -- = elem y (takeWhile (<= y) xs) 
    

    普通人 ELEM 不假定任何内容,因此必须检查其列表参数的每个元素,因此无法处理无限列表。 奥德莱姆 罐头。那么,

    p2 = [y | y <- [3..], not (ordElem y [2,4..])]  -- abstract this as a function, diff a b =
       = diff      [3..]                 [2,4..]    --       = [y | y <- a, not (ordElem y b)]
       -- 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
       -- . 4 . 6 . 8 . 10  . 12  . 14  . 16  . 18  . 20  . 22  .
       = diff [3..] (map (2*)            [2..] )  --  y > 2, so [4,6..] is enough
       = diff [2*k+x | k <- [0..],  x <- [3,4]]   -- "for k from 0 step 1: for x in [3,4]:
              [2*k+x | k <- [0..],  x <- [  4]]   --                            yield (2*k+x)"
       = [     2*k+x | k <- [0..],  x <- [3  ]]   -- 2 = 1*2 = 2*1
       = [3,5..]                                  -- that's 3,5,7,9,11,...
    

    P2 只是上面所有赔率的列表 . 嗯,我们知道 那个 . 下一步怎么办?

    sieve p2 = sieve [3,5..] = sieve (3:[5,7..]) 
                             = 3 : sieve p3
    p3 = [y | y <- [5,7..], rem y 3 /= 0]
       = [y | y <- [5,7..], not (ordElem y [3,6..])]           -- 3,6,9,12,...
       = diff [5,7..] [6,9..]         -- but, we've already removed the multiples of 2, (!)
       -- 5 . 7 . 9 . 11 . 13 . 15 . 17 . 19 . 21 . 23 . 25 . 27 .
       -- . 6 . . 9 . . 12  . . 15 . . 18  . . 21 . . 24  . . 27 .
       = diff [5,7..] (map (3*) [3,5..])                       -- so, [9,15..] is enough
       = diff [2*k+x | k <- [0..], x <- [5]] (map (3*)
              [2*k+x | k <- [0..], x <- [    3]] )
       = diff [6*k+x | k <- [0..], x <- [5,7,9]]               -- 6 = 2*3 = 3*2
              [6*k+x | k <- [0..], x <- [    9]] 
       = [     6*k+x | k <- [0..], x <- [5,7  ]]               -- 5,7,11,13,17,19,...
    

    下一个,

    sieve p3 = sieve (5 : [6*k+x | k <- [0..], x <- [7,11]])
             = 5 : sieve p5
    p5 = [y | y <-        [6*k+x | k <- [0..], x <- [7,11]], rem y 5 /= 0]
       = diff [ 6*k+x | k <- [0..], x <- [7,11]]   (map   (5*)
              [ 6*k+x | k <- [0..], x <- [                  5,       7]]) -- no mults of 2 or 3!
       = diff [30*k+x | k <- [0..], x <- [7,11,13,17,19,23,25,29,31,35]]  -- 30 = 6*5 = 5*6
              [30*k+x | k <- [0..], x <- [                 25,      35]]
       = [     30*k+x | k <- [0..], x <- [7,11,13,17,19,23,   29,31   ]]
    

    这是阿特金筛工作的顺序。无倍数 2, 3 在里面。阿特金和伯恩斯坦工作模块 六十 也就是说,它们是范围的两倍:

    p60 = [ 60*k+x | k <- [0..], x <- [1, 7,11,13,17,19,23,29,31, 37,41,43,47,49,53,59]]
    

    接下来,他们使用一些复杂的定理来了解每个数的一些性质,并相应地处理每个数。整个过程是重复的(a-la“轮子”),周期为 六十 .

    • “只有当且仅当解的个数为 4x^2 + y^2 = n 是奇数,数字是SquareFree。”

    那是什么意思?如果我们知道这个方程的解的个数是奇数,那么它就是质数。 如果 它是方形的。这意味着它没有重复的因素(比如 49, 121, 等)。

    Atkin和Bernstein使用这一点来减少总体操作的数量:针对每个主要任务(从 然后向上),我们将 它的正方形 因此,它们比埃拉托斯滕筛中的要远得多,所以在给定的范围内,它们的数量更少。

    其余规则如下:

    • “只有当且仅当解的个数为 3x^2 + y^2 = n 是奇数,数字是SquareFree。”

    • “只有当且仅当解的个数为 3x^2 − y^2 = n 是奇数,数字是SquareFree。”

    这将处理定义中的所有16个核心数字 p60 .

    参见: Which is the fastest algorithm to find prime numbers?


    埃拉托斯滕筛在制备高达 n O(n log log n) 而阿特金的筛子是 o(n) (把附加的放在一边 log log N 不能很好地扩展的优化)。不过,人们公认的观点是,阿特金筛中的常量因子要高得多,因此它可能只会比32位数字高出很多。( N>2 三十二 ) , if at all .

        3
  •  3
  •   Community CDub    8 年前

    编辑:唯一我还不明白的是x和y变量在伪代码中所指的是什么。有人能帮我解释一下这个吗?

    对于维基百科页面伪代码中“x”和“y”变量的常见用法(或其他更好的atkin筛选实现)的基本解释,您可能会发现 my answer to another related question 乐于助人。

        4
  •  2
  •   Ahmet Kakıcı    10 年前

    这是一个C++实现的AtKin的筛选器,可能会帮助你…

    #include <iostream>
    #include <cmath>
    #include <fstream>
    #include<stdio.h>
    #include<conio.h>
    using namespace std;
    
    #define  limit  1000000
    
    int root = (int)ceil(sqrt(limit));
    bool sieve[limit];
    int primes[(limit/2)+1];
    
    int main (int argc, char* argv[])
    {
       //Create the various different variables required
       FILE *fp=fopen("primes.txt","w");
       int insert = 2;
       primes[0] = 2;
       primes[1] = 3;
       for (int z = 0; z < limit; z++) sieve[z] = false; //Not all compilers have false as the       default boolean value
       for (int x = 1; x <= root; x++)
       {
            for (int y = 1; y <= root; y++)
            {
                 //Main part of Sieve of Atkin
                 int n = (4*x*x)+(y*y);
                 if (n <= limit && (n % 12 == 1 || n % 12 == 5)) sieve[n] ^= true;
                 n = (3*x*x)+(y*y);
                 if (n <= limit && n % 12 == 7) sieve[n] ^= true;
                 n = (3*x*x)-(y*y);
                 if (x > y && n <= limit && n % 12 == 11) sieve[n] ^= true;
            }
       }
            //Mark all multiples of squares as non-prime
       for (int r = 5; r <= root; r++) if (sieve[r]) for (int i = r*r; i < limit; i += r*r) sieve[i] = false;
       //Add into prime array
       for (int a = 5; a < limit; a++)
       {
                if (sieve[a])
                {
                      primes[insert] = a;
                      insert++;
                }
       }
       //The following code just writes the array to a file
       for(int i=0;i<1000;i++){
                 fprintf(fp,"%d",primes[i]);
                 fprintf(fp,", ");
       }
    
       return 0;
     }
    
        5
  •  1
  •   Shravan40    11 年前
    // Title : Seive of Atkin ( Prime number Generator) 
    
    #include <iostream>
    #include <cmath>
    #include <vector>
    
    using namespace std;
    
    int main()
    {
        ios_base::sync_with_stdio(false);
        long long int n;
        cout<<"Enter the value of n : ";
        cin>>n;
        vector<bool> is_prime(n+1);
        for(long long int i = 5; i <= n; i++)
        {
            is_prime[i] = false;
        }
        long long int lim = ceil(sqrt(n));
    
        for(long long int x = 1; x <= lim; x++)
        {
            for(long long int y = 1; y <= lim; y++)
            {
                long long int num = (4*x*x+y*y);
                if(num <= n && (num % 12 == 1 || num%12 == 5))
                {
                    is_prime[num] = true;
                }
    
                num = (3*x*x + y*y);
                if(num <= n && (num % 12 == 7))
                {
                    is_prime[num] = true;
                }
    
                if(x > y)
                {
                    num = (3*x*x - y*y);
                    if(num <= n && (num % 12 == 11))
                    {
                        is_prime[num] = true;
                    }
                }
            }
        }
        // Eliminating the composite by seiveing
        for(long long int i = 5; i <= lim; i++)
        {
            if(is_prime[i])
                for(long long int j = i*i; j <= n; j += i)
                {
                    is_prime[j] = false;
                }
        }
        // Here we will start printing of prime number
       if(n > 2)
       {
           cout<<"2\t"<<"3\t";
       }
       for(long long int i = 5; i <= n; i++)
       {
             if(is_prime[i])
             {
                 cout<<i<<"\t";
             }
        }
        cout<<"\n";
        return 0;
    }