【问题标题】:sieve upto 2 billion gives segmentation fault高达 20 亿的筛子会产生分段错误
【发布时间】:2019-11-21 07:03:22
【问题描述】:

我正在使用这个程序来检查一个数字是否是素数。

使用算法 - 筛子:

#include<bits/stdc++.h>
//#define _max    2000000001
#define _max    20000001
using namespace std;
bool sieve[_max];
void init()
{
    memset(sieve,true,sizeof(sieve));
    sieve[0]=sieve[1]=false;
    for(int i=2;i<_max;i+=2)
    {
        sieve[i]=false;
    }
}
void go_sieve(int n)
{
    n++;
    for(int i=3;i<n;i+=2)
    {
        if(sieve[i]==false)
            continue;
        for(int j=2*i;j<n;j+=i)
            sieve[j]=false;
    }
}
void print(int n)
{
    n++;
    printf("-------------\n");
    for(int i=0;i<n;i++)
    {
        if(sieve[i])
            cout << i << " ";
    }
    printf("\n-------------\n");
}
int main()
{
    init();
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int x;
        scanf("%d",&x);
        go_sieve(x);
        //print(x);
        if(sieve[x])
            printf("Prime\n");
        else
            printf("Not prime\n");
    }
    return 0;
}

现在它可以运行到 2e7 并且非常顺利,但是我想检查到 2e9,如果我将我的 _max 更改为 2000000001 它会给我 segmentation error 并以错误代码退出。

我该如何解决这个问题?

我尝试了一种新的 set 方法:

#include<bits/stdc++.h>
//#define _max    200001
//#define _max    20000001
#define _max    2000000001
using namespace std;
set<int>prime;
set<int>nprime;
void init()
{
    prime.insert(2);
}
void go_sieve()
{
    for(int i=3;i<_max;i+=2)
    {
        if(prime.find(i)==prime.end() && nprime.find(i)==nprime.end())
        {
            prime.insert(i);
            //cout << i << endl;
            for(int j=2*i;j<_max;j+=i)
                nprime.insert(j);
        }
        if(nprime.find(i)!=nprime.end())
            nprime.erase(nprime.find(i));
    }
}
void print()
{
    set<int> ::iterator itt;
    printf("-------------\n");
    for(itt=prime.begin();itt!=prime.end();itt++)
    {
        cout << *itt << " ";
    }
    printf("\n-------------\n");
}
int main()
{
    init();
    go_sieve();
    //print();
    int n;
    scanf("%d",&n);
    while(n--)
    {
        int x;
        scanf("%d",&x);
        if(prime.find(x)!=prime.end())
            printf("Prime\n");
        else
            printf("Not prime\n");
    }
    return 0;
}

目标是在 512MB~1GB 内存内执行。

【问题讨论】:

  • 程序对数组使用了大量内存(大概8G)。也许你的系统无法处理。你有多少内存?是否允许过度提交?
  • 运行示例ideone.com/KJshJI
  • 有些系统对静态分配的大小有限制,您是否尝试过使用 std::vector 而不是数组?您还应该确保编译为 64 位,否则您将被限制为最多 4gb 的内存
  • 鉴于#include&lt;bits/stdc++.h&gt; 的可怕用途,他必须使用 gcc,所以 bool 可能是 1 个字节
  • 大多数 C++ 运行时库提供 std::vector 的专门实现,每个元素仅使用 1 位。

标签: c++ segmentation-fault primes sieve-of-eratosthenes


【解决方案1】:

如果你想枚举大范围的素数,你应该使用segmented Sieve of Eratosthenes;它会更快(由于缓存效果)并且使用更少的内存。

如果您只想确定一个数字是素数还是几个数字,筛分是一种可怕的方法。仅当您对整个数字范围感兴趣时才应使用筛分。对于高达 10 亿的 n,试用除法很简单,而且可能足够快。对于较大的数字,Miller-Rabin 检验或 Baillie-Wagstaff 检验可能更好。

【讨论】:

  • 对于小于 82 位的整数,有 Miller-Rabin 的确定性版本。
【解决方案2】:

我无法在我的系统上重现此内容。我的猜测是,这与系统相关限制有关。

您将sieve 声明为全局数组(静态存储持续时间),并且它很大(即 2000000001 * sizeof(bool) - 可能是 2-8G,具体取决于 sizeof bool)。也许您的系统无法处理。

尝试使用动态分配,而不是全局数组:

// bool sieve[_max]; comment out this
bool* sieve = NULL;

...
...

int main()
{
    sieve = (bool*)malloc(_max * sizeof *sieve);
    if (sieve == NULL)
    {
        // out of memory
        exit(1);
    }

    ...

也就是说:

您的代码是 C++,但您的风格更像 C。

在 C++ 中,您可能会改用 std::vector。这会让一切变得更容易。

顺便说一句:还要避免使用全局变量。而是在 main 中定义向量(或动态数组)并将其通过引用传递给函数。

【讨论】:

  • 我的系统是512MB的虚拟机,为了优化我已经编辑了代码并使用了set,你能再看看吗
【解决方案3】:

您的系统可能达到了内存限制,导致分段错误。

但是,您不需要这么大的数组。使用埃拉托色尼筛法,您需要计算高达x 的数字。您可以使用std::vector 代替数组,并在计算更多数字时增加其大小。这应该允许您计算一些数字,但如果数字很大,您将再次达到内存限制。

您还可以使用一些算法来要求您存储更少的数字。要确定x 是否为素数,您只需与小于x 平方根的素数进行比较。您不必存储不是素数的数字。使用x = 1e10,您只需要存储5e8 numbers

这里是一些向量的例子(可能不是最优的):

#include <iostream>
#include <vector>
#include <algorithm>
#include <cmath>

std::vector<int> primes = {2};

void calculate(int x) {
    const int largest_prime = primes.back();
    if (largest_prime >= x) {
        // Already calculated
        return; 
    }
    for (size_t i = largest_prime + 1; i <= x; i++) {
        bool not_prime = false;
        for (size_t j = 0; j < primes.size(); j++) {
            if (i % primes[j] == 0) {
                not_prime = true;
                break;
            }
        }
        if (!not_prime) {
            primes.push_back(i);
        }
    }
}

bool check(int x) {
    calculate(x);
    return std::find(primes.begin(), primes.end(), x) != primes.end();
}

int main() {
    std::cout << check(15) << std::endl;
    std::cout << check(256699) << std::endl;
}

【讨论】:

  • 是的,你能建议我一些更好的方法吗?我已经编辑了代码并更新了问题,但内存不是问题。你能帮我吗?
  • @Danial 我添加了一个使用矢量的示例。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-27
  • 2016-05-18
  • 1970-01-01
  • 1970-01-01
  • 2017-12-27
  • 2013-05-14
相关资源
最近更新 更多