埃拉托色尼筛算法

Posted

技术标签:

【中文标题】埃拉托色尼筛算法【英文标题】:Sieve of Eratosthenes algorithm 【发布时间】:2009-12-23 19:31:43 【问题描述】:

我目前正在阅读“编程:使用 C++ 的原理和实践”,在第 4 章中有一个练习:

我需要编写一个程序来使用计算 1 到 100 之间的素数。

这是我想出的程序:

#include <vector>
#include <iostream>

using namespace std;

//finds prime numbers using Sieve of Eratosthenes algorithm
vector<int> calc_primes(const int max);

int main()

    const int max = 100;

    vector<int> primes = calc_primes(max);

    for(int i = 0; i < primes.size(); i++)
    
        if(primes[i] != 0)
            cout<<primes[i]<<endl;
    

    return 0;


vector<int> calc_primes(const int max)

    vector<int> primes;

    for(int i = 2; i < max; i++)
    
        primes.push_back(i);
    

    for(int i = 0; i < primes.size(); i++)
    
        if(!(primes[i] % 2) && primes[i] != 2)
             primes[i] = 0;
        else if(!(primes[i] % 3) && primes[i] != 3)
             primes[i]= 0;
        else if(!(primes[i] % 5) && primes[i] != 5)
             primes[i]= 0;
        else if(!(primes[i] % 7) && primes[i] != 7)
             primes[i]= 0;
       

    return primes;

不是最好的或最快的,但我还处于起步阶段,对 C++ 了解不多。

现在的问题是,直到max 不大于500,所有值都打印在控制台上,如果max &gt; 500 不是所有内容都打印出来。

我是不是做错了什么?

P.S.:也非常感谢任何建设性的批评。

【问题讨论】:

这不是埃拉托色尼算法的筛子。您似乎只筛选可被 2、3、5 和 7 整除的值,但可被其他素数整除的值呢?提示:你根本不需要模数。 en.wikipedia.org/wiki/Sieve_of_Eratosthenes 您能否提供一个开箱即用的编译示例? @UncleBens-我这样做是因为***上的示例从表格中删除了所有 2、3、5、7 的倍数,但我想我理解错了。 参见。 Melissa O'Neill 关于筛子算法的真实案例,以及 lambda-the-ultimate.org/node/3127 上的 Lambda the Ultimate 讨论@ @AndreyT:帖子的标题是“”。上面的代码没有实现 Sieve。 【参考方案1】:

我不知道为什么你没有得到所有的输出,因为看起来你应该得到所有的东西。你缺少什么输出?

筛子用错了。类似的东西

vector<int> sieve;
vector<int> primes;

for (int i = 1; i < max + 1; ++i)
   sieve.push_back(i);   // you'll learn more efficient ways to handle this later
sieve[0]=0;
for (int i = 2; i < max + 1; ++i)    // there are lots of brace styles, this is mine
   if (sieve[i-1] != 0) 
      primes.push_back(sieve[i-1]);
      for (int j = 2 * sieve[i-1]; j < max + 1; j += sieve[i-1]) 
          sieve[j-1] = 0;
      
   

将实施筛子。 (上面的代码写在我的脑海中;不保证可以工作甚至编译。我不认为它有任何内容在第 4 章末尾没有涵盖。)

像往常一样返回primes,并打印出全部内容。

【讨论】:

说我将最大值初始化为 10000,它只打印从 8000 到 9000 的整数。 你如何检查这个?您是打印到窗口吗?一些输出可能会滚动到顶部。 我正在使用 cout 打印到控制台,奇怪的是它从 8000 开始打印。 使用新的筛子实现,它打印的一切都很好:=)【参考方案2】:

将筛子视为一组。 按顺序浏览集合。对于 thesive 中的每个值,删除所有可被它整除的数字。

#include <set>
#include <algorithm>
#include <iterator>
#include <iostream>


typedef std::set<int>   Sieve;

int main()

    static int const max = 100;

    Sieve   sieve;

    for(int loop=2;loop < max;++loop)
    
        sieve.insert(loop);
    


    // A set is ordered.
    // So going from beginning to end will give all the values in order.
    for(Sieve::iterator loop = sieve.begin();loop != sieve.end();++loop)
    
        // prime is the next item in the set
        // It has not been deleted so it must be prime.
        int             prime   = *loop;

        // deleter will iterate over all the items from
        // here to the end of the sieve and remove any
        // that are divisable be this prime.
        Sieve::iterator deleter = loop;
        ++deleter;

        while(deleter != sieve.end())
        
            if (((*deleter) % prime) == 0)
            
                // If it is exactly divasable then it is not a prime
                // So delete it from the sieve. Note the use of post
                // increment here. This increments deleter but returns
                // the old value to be used in the erase method.
                sieve.erase(deleter++);
            
            else
            
                // Otherwise just increment the deleter.
                ++deleter;
            
        
    

    // This copies all the values left in the sieve to the output.
    // i.e. It prints all the primes.
    std::copy(sieve.begin(),sieve.end(),std::ostream_iterator<int>(std::cout,"\n"));


【讨论】:

【参考方案3】:

来自Algorithms and Data Structures:

void runEratosthenesSieve(int upperBound) 
      int upperBoundSquareRoot = (int)sqrt((double)upperBound);
      bool *isComposite = new bool[upperBound + 1];
      memset(isComposite, 0, sizeof(bool) * (upperBound + 1));
      for (int m = 2; m <= upperBoundSquareRoot; m++) 
            if (!isComposite[m]) 
                  cout << m << " ";
                  for (int k = m * m; k <= upperBound; k += m)
                        isComposite[k] = true;
            
      
      for (int m = upperBoundSquareRoot; m <= upperBound; m++)
            if (!isComposite[m])
                  cout << m << " ";
      delete [] isComposite;

【讨论】:

问题具体要求使用vector&lt;&gt; 在哪里?他的实现碰巧使用了它,但我在他的问题中没有看到任何关于 vector 的具体问题。【参考方案4】:

有趣的是,似乎没有人回答您关于输出问题的问题。我在代码中看不到任何会影响输出的内容,具体取决于 max 的值。

对于它的价值,在我的 Mac 上,我得到了所有的输出。这当然是错误的,因为算法不正确,但我确实得到了所有输出。您没有提及您在哪个平台上运行,如果您继续遇到输出问题,这可能会很有用。


这是您的代码的一个版本,根据实际的 Sieve 算法进行了最低限度的修改。

#include <vector>
#include <iostream>

using namespace std;

//finds prime numbers using Sieve of Eratosthenes algorithm
vector<int> calc_primes(const int max);

int main()

    const int max = 100;

    vector<int> primes = calc_primes(max);

    for(int i = 0; i < primes.size(); i++)
    
        if(primes[i] != 0)
            cout<<primes[i]<<endl;
    

    return 0;


vector<int> calc_primes(const int max)

    vector<int> primes;

    // fill vector with candidates
    for(int i = 2; i < max; i++)
    
        primes.push_back(i);
    

    // for each value in the vector...
    for(int i = 0; i < primes.size(); i++)
    
        //get the value
        int v = primes[i];

        if (v!=0) 
            //remove all multiples of the value
            int x = i+v;
            while(x < primes.size()) 
                primes[x]=0;
                x = x+v;
            
        
    
    return primes;

【讨论】:

【参考方案5】:

在下面的代码片段中,数字在插入vector 之前会被过滤。除数来自向量。

我也通过 reference 传递向量。这意味着巨大的向量不会从函数复制到调用者。 (大块内存需要很长时间才能复制)

vector<unsigned int> primes;

void calc_primes(vector<unsigned int>& primes, const unsigned int MAX)

    // If MAX is less than 2, return an empty vector
    // because 2 is the first prime and can't be placed in the vector.
    if (MAX < 2)
    
         return;
    

    // 2 is the initial and unusual prime, so enter it without calculations.
    primes.push_back(2);
    for (unsigned int number = 3; number < MAX; number += 2)
    
        bool is_prime = true;
        for (unsigned int index = 0; index < primes.size(); ++index)
        
            if ((number % primes[k]) == 0)
            
                is_prime = false;
                break;
            
        

        if (is_prime)
        
            primes.push_back(number);
        
        

这不是最有效的算法,但它遵循 Sieve 算法。

【讨论】:

第一条评论说你返回 2 作为第一个素数,但你不是。为什么你没有 MAX? 其实评论说我回来是因为#2是第一个素数,MAX小于2。【参考方案6】:

下面是我的版本,它基本上使用 bool 的位向量,然后通过奇数和快速添加来查找倍数以设置为 false。最后构造一个向量并将其返回给素数值的客户端。

std::vector<int>  getSieveOfEratosthenes ( int max )

  std::vector<bool> primes(max, true);
  int sz = primes.size();

  for ( int i = 3; i < sz ; i+=2 )
    if ( primes[i] ) 
      for ( int j = i * i; j < sz; j+=i)
        primes[j] = false;

  std::vector<int> ret;
  ret.reserve(primes.size());
  ret.push_back(2);

  for ( int i = 3; i < sz; i+=2 )
    if ( primes[i] )
      ret.push_back(i);

  return ret;

【讨论】:

我有一个错误,max=1000000。它在第一个循环中。我将代码替换为: int m = (int)sqrt((double)sz); for (int i = 3; i 【参考方案7】:

这是一个使用bool类型的简洁、解释清楚的实现:

#include <iostream>
#include <cmath>

void find_primes(bool[], unsigned int);
void print_primes(bool [], unsigned int);

//=========================================================================
int main() 

    const unsigned int max = 100;
    bool sieve[max];

    find_primes(sieve, max);

    print_primes(sieve, max);

//=========================================================================

/*
    Function: find_primes()
    Use: find_primes(bool_array, size_of_array);

    It marks all the prime numbers till the
    number: size_of_array, in the form of the
    indexes of the array with value: true.

    It implemenets the Sieve of Eratosthenes,
    consisted of:

    a loop through the first "sqrt(size_of_array)"
    numbers starting from the first prime (2).

    a loop through all the indexes < size_of_array,
    marking the ones satisfying the relation i^2 + n * i
    as false, i.e. composite numbers, where i - known prime 
    number starting from 2.
*/
void find_primes(bool sieve[], unsigned int size)

    // by definition 0 and 1 are not prime numbers
    sieve[0] = false;
    sieve[1] = false;

    // all numbers <= max are potential candidates for primes
    for (unsigned int i = 2; i <= size; ++i)
    
        sieve[i] = true;
    

    // loop through the first prime numbers < sqrt(max) (suggested by the algorithm)
    unsigned int first_prime = 2;
    for (unsigned int i = first_prime; i <= std::sqrt(double(size)); ++i)
    
        // find multiples of primes till < max
        if (sieve[i] = true)
        
            // mark as composite: i^2 + n * i 
            for (unsigned int j = i * i; j <= size; j += i)
            
                sieve[j] = false;
            
        
    


/*
    Function: print_primes()
    Use: print_primes(bool_array, size_of_array);

    It prints all the prime numbers, 
    i.e. the indexes with value: true.
*/
void print_primes(bool sieve[], unsigned int size)

    // all the indexes of the array marked as true are primes
    for (unsigned int i = 0; i <= size; ++i)
    
        if (sieve[i] == true) 
        
            std::cout << i <<" ";
        
    

覆盖数组的情况。 std::vector 实现将包括一些小的更改,例如将函数减少为一个参数,通过引用传递向量,循环将使用向量 size() 成员函数而不是缩减参数。

【讨论】:

【参考方案8】:

这是我实现的埃拉托色尼筛算法的更高效版本。

#include <iostream>
#include <cmath>
#include <set>

using namespace std;

void sieve(int n)
    set<int> primes;
    primes.insert(2);
    for(int i=3; i<=n ; i+=2)
        primes.insert(i);
           

    int p=*primes.begin();
    cout<<p<<"\n";
    primes.erase(p);

    int maxRoot = sqrt(*(primes.rbegin()));

    while(primes.size()>0)
        if(p>maxRoot)
            while(primes.size()>0)
                p=*primes.begin();
                cout<<p<<"\n";
                primes.erase(p);        
            
            break;
        

        int i=p*p;  
        int temp = (*(primes.rbegin()));
        while(i<=temp)
            primes.erase(i);
            i+=p;
            i+=p;
        
        p=*primes.begin();
        cout<<p<<"\n";
        primes.erase(p);
    


int main()
    int n;
    n = 1000000;
    sieve(n);                
    return 0;

【讨论】:

【参考方案9】:

这是我的实现,但不确定是否 100% 正确: http://pastebin.com/M2R2J72d

#include<iostream>
#include <stdlib.h> 

using namespace std;
void listPrimes(int x);

int main() 

    listPrimes(5000);


void listPrimes(int x) 
    bool *not_prime = new bool[x];
    unsigned j = 0, i = 0;

    for (i = 0; i <= x; i++) 
        if (i < 2) 
            not_prime[i] = true;
         else if (i % 2 == 0 && i != 2) 
            not_prime[i] = true;
        
    

    while (j <= x) 
        for (i = j; i <= x; i++) 
            if (!not_prime[i]) 
                j = i;
                break;
            
        
        for (i = (j * 2); i <= x; i += j) 
            not_prime[i] = true;
        
        j++;
    

    for ( i = 0; i <= x; i++) 
        if (!not_prime[i])
            cout << i << ' ';
    

    return;

【讨论】:

【参考方案10】:

我现在正在关注同一本书。我想出了以下算法的实现。

#include<iostream>
#include<string>
#include<vector>
#include<algorithm>
#include<cmath>
using namespace std;
inline void keep_window_open()  char ch; cin>>ch; 

int main ()

    int max_no = 100;

    vector <int> numbers (max_no - 1);
    iota(numbers.begin(), numbers.end(), 2);

    for (unsigned int ind = 0; ind < numbers.size(); ++ind)
    
        for (unsigned int index = ind+1; index < numbers.size(); ++index)
        
            if (numbers[index] % numbers[ind] == 0)
            
                numbers.erase(numbers.begin() + index);
            
        
    
    cout << "The primes are\n";
    for (int primes: numbers)
    
        cout << primes << '\n';
    

【讨论】:

【参考方案11】:

这是我的版本:

#include "std_lib_facilities.h"
//helper function:check an int prime, x assumed positive.
bool check_prime(int x) 
    bool check_result = true;
    for (int i = 2; i < x; ++i)
        if (x%i == 0)
            check_result = false;
            break;
        
    
    return check_result;


//helper function:return the largest prime smaller than n(>=2).
int near_prime(int n) 
    for (int i = n; i > 0; --i) 
        if (check_prime(i))  return i; break; 
    



vector<int> sieve_primes(int max_limit) 
    vector<int> num;
    vector<int> primes;
    int stop = near_prime(max_limit);
    for (int i = 2; i < max_limit+1; ++i)  num.push_back(i); 
    int step = 2;
    primes.push_back(2);
    //stop when finding the last prime
    while (step!=stop)
        for (int i = step; i < max_limit+1; i+=step) num[i-2] = 0; 
        //the multiples set to 0, the first none zero element is a prime also step
        for (int j = step; j < max_limit+1; ++j) 
            if (num[j-2] != 0)  step = num[j-2]; break; 
        
        primes.push_back(step);
    
    return primes;


int main() 
    int max_limit = 1000000;
    vector<int> primes = sieve_primes(max_limit);
    for (int i = 0; i < primes.size(); ++i) 
        cout << primes[i] << ',';
    

【讨论】:

【参考方案12】:

这是一个经典的方法,

int main()

    int max = 500;
    vector<int> array(max); // vector of max numbers, initialized to default value 0

    for (int i = 2; i < array.size(); ++ i) // loop for rang of numbers from 2 to max
    
        // initialize j as a composite number; increment in consecutive composite numbers
        for (int j = i * i; j < array.size(); j +=i)
            array[j] = 1;  // assign j to array[index] with value 1
    

    for (int i = 2; i < array.size(); ++ i) // loop for rang of numbers from 2 to max
        if (array[i] == 0)  // array[index] with value 0 is a prime number
        cout << i << '\n';  // get array[index] with value 0

    return 0;

【讨论】:

【参考方案13】:

我想我参加这个聚会迟到了,但我和你读的是同一本书,这就是我想出的解决方案!随意提出建议(您或任何人!),对于我在这里看到的,我们中的几个人提取了操作以了解一个数字是否是另一个函数的倍数。

#include "../../std_lib_facilities.h"

bool numIsMultipleOf(int n, int m) 
  return n%m == 0;


int main() 
  vector<int> rawCollection = ;
  vector<int> numsToCheck = 2,3,5,7;

  // Prepare raw collection
  for (int i=2;i<=100;++i) 
    rawCollection.push_back(i);
  

  // Check multiples
  for (int m: numsToCheck) 
    vector<int> _temp = ;

    for (int n: rawCollection) 
      if (!numIsMultipleOf(n,m)||n==m) _temp.push_back(n);
    
    rawCollection = _temp;
  

  for (int p: rawCollection) 
    cout<<"N("<<p<<")"<<" is prime.\n";
  

  return 0;

【讨论】:

【参考方案14】:

试试这个代码,使用java question bank会对你有用

import java.io.*;

class Sieve



    public static void main(String[] args) throws IOException

    

        int n = 0, primeCounter = 0;

        double sqrt = 0;

        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));


        System.out.println(“Enter the n value : ”);

        n = Integer.parseInt(br.readLine());

        sqrt = Math.sqrt(n);

        boolean[] prime = new boolean[n];

        System.out.println(“\n\nThe primes upto ” + n + ” are : ”);

        for (int i = 2; i<n; i++)

        

            prime[i] = true;

        

        for (int i = 2; i <= sqrt; i++)

        

            for (int j = i * 2; j<n; j += i)

            

                prime[j] = false;

            

        

        for (int i = 0; i<prime.length; i++)

        

            if (prime[i])

            

                primeCounter++;

                System.out.print(i + ” “);

            

        

        prime = new boolean[0];

    


【讨论】:

以上是关于埃拉托色尼筛算法的主要内容,如果未能解决你的问题,请参考以下文章

埃拉托色尼筛法(Sieve of Eratosthenes)求素数。

优化埃拉托色尼筛

埃拉托色尼筛 - 主要因素

埃拉托色尼筛的问题

埃拉托色尼筛法及线性筛法

埃拉托色尼筛 - 寻找素数 Python