高达20亿的筛子会产生分割错误

sieve upto 2 billion gives segmentation fault

本文关键字:分割 错误 20亿 高达      更新时间:2023-10-16

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

使用算法-筛选:

#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("Primen");
else
printf("Not primen");
}
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("Primen");
else
printf("Not primen");
}
return 0;
}

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

如果你想枚举大范围的素数,你应该使用Eratosthenes的分段筛;它将更快(由于缓存效应(并且使用更少的内存。

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

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

您将sieve声明为全局数组(静态存储持续时间(,并且它是巨大的(即2000000001*sizeof(bool(-根据sizeof bool的不同,可能是2-8G(。也许你的系统无法处理。

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

// 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。这会让一切变得容易多了。

BTW:还要避免全球化。相反,在main中定义向量(或动态数组(,并通过引用函数将其传递。

您的系统可能达到了内存限制,这导致了分段故障。

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

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

以下是矢量的一些例子(可能不是最优的(:

#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;
}