繁体   English   中英

c ++筛选了Eratosthenes我的代码很慢

[英]c++ sieve of Eratosthenes my code is slow

我试图找到低于4亿的素数,但即使只有4千万我的代码需要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;
}

通过改变两件事,我在计算机上花了10秒钟才能运行到半秒钟。 首先,我猜你没有在启用优化的情况下编译它。 这让我从10秒减少到1秒。 其次,矢量c是不必要的。 你的代码中有c[i] ,你可以用i+2替换它。 这将使其运行速度提高两倍。

  1. 删除矢量c,你不需要它。
  2. 在开始时创建具有已知大小的向量k。 从性能的角度来看,通过调用push_back()重复向向量追加元素是一个非常糟糕的主意,因为它可能导致重复的内存重新分配和复制。
  3. http://primesieve.org/segmented_sieve.html - 分段版本的灵感。
  4. 您可以跳过处理代码审查的2和3 链接的倍数
  5. 看起来您在编译器优化标志设置中遇到了一些问题。 也许您没有将配置从调试更改为发布。 你的发布加速与调试相比是什么?

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

    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;

  }

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

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 s上操作时根本不使用push_back

在此示例中, vectorint因此在完成时您将拥有实际的素数列表。

输出:

2433654
Elapsed = 2160

以上都是调试模式编号。

在发布模式中,最好的是上面第二种和第三种技术的组合,使用带有bool矢量的数字,如果你不关心实际的素数到底是什么:

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

请注意,在发布模式下,我5年前的笔记本电脑上的原始代码只需要大约1秒钟,因此您可能正在调试模式下运行。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM