优化埃拉托色尼筛

Posted

技术标签:

【中文标题】优化埃拉托色尼筛【英文标题】:Optimizing Sieve of Eratosthenes 【发布时间】:2017-10-16 16:20:38 【问题描述】:

我目前正在尝试进一步优化我的筛子。我必须使用 eratosthenes 筛计算两个数字之间的素数,我知道需要工作的两个数字是 2000000000000 和 2000000100000。由于运行时间过长,我当前的代码出现分段错误。任何优化方面的帮助将不胜感激:

#include <iostream>
#include <cmath>
using namespace std;

double Sieve(long long a, long long b)

    //Create array of type bool
    bool *prime;
    prime = new bool[b];

    //Set all values in array to true
    for (long i = 0; i < b; i++)
        prime[i] = true;
    


    long count = 0;

    //Runs through main Sieve algorithm
    for (long x = 2*2; x <= (b); x += 2 )
        prime[x] = false;
    
    for (long x = 3; x <= sqrt(b); x = 2*x )
        if (prime[x] == true)
            for (long y = pow(x,2); y <= b; y += x)
                prime[y] = false;
            
        

    

    //Loop to print out and count how many primes are present
    for (long x = a; x <= b; x++)
        if(prime[x] == true)
            count++;
        
    
    return count;


int main()
    int a, b;
    cout << "Please enter two numbers separated by one space" << endl;
    cin >> a >> b;
    cout << Sieve(1,20) << endl;
    cout << Sieve(a,b) << endl;

【问题讨论】:

在访问不允许访问的内存并且不是由于运行时间过长时会发生分段错误。 你能发布错误吗? 您似乎在这里缺少一个功能:for (long x = 2*2; x &lt;= (b); x += 2 )。这将导致x == b 出现分段错误。 【参考方案1】:

您正在尝试分配太多的内存方式new 可能失败并抛出std::bad_alloc exception。如果您不注意,未捕获的异常可能类似于分段错误。

要解决这个问题,您将需要 两个 数组 - 一个用于大小为 100001 的输出范围,一个用于确定最大到 sqrt(b) 的素数。

正如另一个答案所指出的,使用 std::vector&lt;bool&gt; 还将您的内存需求减少 8。这不足以消除需要两个数组,但仍然有很大帮助。有时人们会反对vector&lt;bool&gt;,因为它有一些奇怪之处,但为此目的它是完美的。

【讨论】:

@manni66 你当然是对的。我在想malloc 我猜。我不记得我上次尝试失败的new 是什么时候了。 不管newmalloc 的语义如何,这都是正确的,应该可以让您进行练习。同时,您可以玩一些进一步的预处理技巧,例如识别除 2 以外的所有数字(不在您的集合中)都不是素数,因此只需要分配一半的元素。对于较小的数字,您可以玩更多的技巧,例如识别所有 3 的倍数(如果不是模数,请进行奇特的位检查),并获得额外 1/6 的元素,您不需要存储。当然,成本是打印结果时的簿记。 ^^ "所有 [偶数] 数除了 2 都不是素数". 110,000 个素数就足够了。【参考方案2】:

您可能内存不足。使用 bool 数组可能会为每个条目分配 1 个字节,如果您允许使用 std::vector,则 std::vector&lt;bool&gt; 仅使用单个位。

【讨论】:

有趣,我只是假设它是由于运行时间太长。我以前从未使用过向量,但它会像您描述的那样将我的 bool 数组更改为向量 bool 数组那么简单吗? @Knoep - 这是硬件问题; - ) @MartinBeckett 最后只是强调了您对 OP 陷入困境的原因的猜测非常正确:) @Knoep,我是物理学家而不是计算机科学家。 CPU/内存/存储/带宽永远不够!【参考方案3】:

2000000000000 这对于数组来说太大了。而是使用位集。您可以以更快的方式生成更大的素数。以这种方式在全局范围内声明位集,它可以容纳 10^7。但是您可以使用分段筛算法使其适用于 2000000000000。在这种情况下 sqrt(2000000000000) = 1414213.这就是为什么您必须生成 2 到 1414214 之间的所有素数的原因。然后使用分段筛算法

#include <bits/stdc++.h>
#define bitset_range 1414214
typedef long long int lli;
typedef long int li;
using namespace std;
template <typename T>
void printer(T a) 
    cout << a << endl;

vector<li> stock_prime;
bitset<bitset_range + 1> numbers;
int main() 
    lli start, stop;
    //normal seieve
    numbers.set();
    numbers.reset(0);
    numbers.reset(1);
    for (li i = 2; i <= bitset_range; i++) 
        if (numbers.test(i)) 
            for (li j = i * i; j <= bitset_range; j+= i) 
                numbers.reset(j);
            
            stock_prime.push_back(i);
        
    
    //for_each(stock_prime.begin(), stock_prime.end(), printer<int>);
    cout << "Size: " << stock_prime.size() << endl;

    //now use segmented seive algorithm here remember to use bitset rather than bool array



    return 0;

【讨论】:

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

埃拉托色尼筛 - 主要因素

埃拉托色尼筛的问题

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

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

通过埃拉托色尼筛算法(C++)查找素数

使用埃拉托色尼筛法找到第 n 个素数