c++ 埃拉托色尼筛法我的代码很慢

标签 c++ time-complexity sieve-of-eratosthenes

我试图找出 4 亿以下素数的数量,但即使只有 4000 万,我的代码也需要 8 秒才能运行。我做错了什么?

我该怎么做才能让它更快?

#include<iostream>
#include<math.h>
#include<vector>
using namespace std;
int main()
{
    vector<bool> k;                         
    vector<long long int> c;                
    for (int i=2;i<40000000;i++)
    {
        k.push_back(true);                  
        c.push_back(i);
    }

    for ( int i=0;i<sqrt(40000000)+1;i++)                            
    {                                                               
        if (k[i]==true)                                              
       {                                                            
           for (int j=i+c[i];j<40000000;j=j+c[i])                  
           {                                                       
               k[j]=false; 
           }
       }
    }
    vector <long long int> arr;
    for ( int i=0;i<40000000-2;i++)
    {
        if (k[i]==true)
        {
            arr.push_back(c[i]);
        }
    }
    cout << arr.size() << endl ;
    return 0;
}

最佳答案

我在下面分析了您的代码以及一个简单的调整。调整速度是原来的两倍多:

    auto start = std::chrono::high_resolution_clock::now();

    //original version
    vector<bool> k;                         
    vector<long long int> c;                
    for (int i=2;i<40000000;i++)
      {
        k.push_back(true);                  
        c.push_back(i);
      }

    for ( int i=0;i<sqrt(40000000)+1;i++)                            
      {                                                               
        if (k[i]==true)                                              
          {                                                            
            for (int j=i+c[i];j<40000000;j=j+c[i])                  
              {                                                       
                k[j]=false; 
              }
          }
      }
    vector <long long int> arr;
    for ( int i=0;i<40000000-2;i++)
      {
        if (k[i]==true)
          {
            arr.push_back(c[i]);
          }
      }
    cout << arr.size() << endl ;


    auto end1 = std::chrono::high_resolution_clock::now();
    std::cout << "Elapsed = " << 
      std::chrono::duration_cast<std::chrono::milliseconds>(end1 - start).count() <<
      std::endl;

  }

  {

    auto begin = std::chrono::high_resolution_clock::now();

    //new version

    const long limit{40000000};
    vector<bool> k(limit-1,true);  

    //k[0] is the number 0
    k[0]=false; k[1]=false;

    auto sq = sqrt(limit) + 1;

    //start at the number 2
    for ( int i=2;i<sq;i++)                            
      {                                                               
        if (k[i]==true)                                              
          {                                                            
            for (int j=i+i;j<limit;j+=i)                  
              {                                                       
                k[j]=false; 
              }
          }
      }


    vector <long long int> arr;
    for ( int i=0;i<limit-2;i++)
      {
        if (k[i]==true)
          {
            arr.push_back(i);
          }
      }
    cout << arr.size() << endl ;



    auto stop = std::chrono::high_resolution_clock::now();
    std::cout << "Elapsed = " << 
      std::chrono::duration_cast<std::chrono::milliseconds>(stop - begin).count() <<
      std::endl;

  }

这是 Debug模式下的输出(以毫秒为单位):

2433654
Elapsed = 5787
2433654
Elapsed = 2432

两者有相同的结果,第二个要快得多。

这是另一个使用一些不错的 C++ 特性的版本(需要更少的代码),它比上面的第二个版本快大约 11%:

    auto begin = std::chrono::high_resolution_clock::now();

    const long limit{40000000};
    vector<int> k(limit-1,0);

    //fill with sequence of integers
    std::iota(k.begin(),k.end(),0);

    //k[0] is the number 0
    //integers reset to 0 are not prime
    k[0]=0; k[1]=0;

    auto sq = sqrt(limit) + 1;

    //start at the number 2
    for (int i=2;i<sq;i++)                            
      {                                                               
        if (k[i])
          {                                                            
            for (int j=i+i;j<limit;j+=i)                  
              {                                                       
                k[j]=0; 
              }
          }
      }

    auto results = std::remove(k.begin(),k.end(),0);

    cout << results - k.begin() << endl ;


    auto stop = std::chrono::high_resolution_clock::now();
    std::cout << "Elapsed = " << 
      std::chrono::duration_cast<std::chrono::milliseconds>(stop - begin).count() <<
      std::endl;

  }

请注意,在您的原始版本中,您在三个不同的地方 push_back,而这种现代习语的使用在对 vector 进行操作时根本不会使用 push_back s.

在这个例子中,vectorint 的,这样当你完成时你就有了实际的素数列表。

输出:

2433654
Elapsed = 2160

以上都是 Debug模式编号。

在 Release 模式下,最好是结合上面的第二和第三种技术,使用带有 bool vector 的数字,如果你不关心最后实际的素数是什么:

2433654
Elapsed = 1098
2433654
Elapsed bool remove= 410
2433654
Elapsed = 779

请注意,在我使用了 5 年的笔记本电脑上,您的原始代码在 Release 模式下只需要大约 1 秒,因此您可能在 Debug 模式下运行。

关于c++ 埃拉托色尼筛法我的代码很慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37172597/

相关文章:

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

c++ - 有没有办法将回调附加到 C++ 对象成员?

c++ - 在 C++ 中查找小于特定整数值的最接近浮点值?

c++ - 我的开关不工作

arrays - 股票价格的最大利润

time-complexity - 这个 for 循环的复杂度是多少,for (int j = i; j < n; j++)?

C++ 静态而非动态多态性

算法复杂度 n*(m+n)^2

java - Eratosthenes 筛法不会筛选素数

python - 使用 Eratosthenes 筛法找出从 2 到 n 的所有素数