使用C中的筛选方法列出最多20亿的素数

时间:2013-01-08 09:16:00

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

我尝试使用Sieve Eratosthenes方法列出最多20亿的素数。这是我用的!

我面临的问题是,我无法超越一千万个数字。当我尝试时,它说“分段错误”。我在互联网上搜索找到原因。有些网站说,这是编译器本身的内存分配限制。有人说,这是一个硬件限制。我使用的是安装了4GB RAM的64位处理器。请建议我列出它们的方法。

#include <stdio.h>
#include <stdlib.h>

#define MAX 1000000
long int mark[MAX] = {0};

int isone(){
   long int i;
   long int cnt = 0;
   for(i = 0; i < MAX ; i++){
      if(mark[i] == 1)
         cnt++;
   }
   if(cnt == MAX)
      return 1;
   else
      return 0;
}



int main(int argc,char* argv[]){
   long int list[MAX];
   long int s = 2;
   long int i;
   for(i = 0 ; i < MAX ; i++){
      list[i] = s;
      s++;
   }
   s = 2;
   printf("\n");

   while(isone() == 0){
      for(i = 0 ; i < MAX ; i++){
         if((list[i] % s) == 0)
            mark[i] = 1;
      }
      printf(" %lu ",s);
      while(mark[++s - 2] != 0);
   }

   return 1;
}

2 个答案:

答案 0 :(得分:6)

long int mark[1000000]执行堆栈分配,这不是您想要的内存量。尝试long int *mark = malloc(sizeof(long int) * 1000000)来分配堆内存。这将使你超过〜1万个数组元素。

记住free内存,如果你不再使用它。如果你不知道malloc或free,请阅读这些函数的联机帮助页(手册),可以在任何linux终端上通过man 3 mallocman 3 free获得。 (或者您可以只是谷歌man malloc

编辑:使calloc(1000000, sizeof(long int))具有0初始化数组,这可能更好。

此外,您可以将数组的每个元素用作位掩码,以便每位存储一个标记,而不是每个sizeof(long int)字节。我建议对数组元素使用固定宽度整数类型,如uint32_t,然后将数组的(n % 32)'元素中的(n / 32)'位设置为1将第n个元素设置为1。

您可以使用:

设置整数i的第n位
uint32_t i = 0;
i |= ((uint32_t) 1) << n

假设您从0开始计数。

使你在uint32_t位掩码数组上的set操作得到一个数字n:

mask[n / 32] |= ((uint32_t)1) << (n % 32)

为32位类型节省了大约99%的内存。玩得开心:D

这里使用的另一种更先进的方法是素轮分解,这基本上意味着您事先将2,3和5(甚至可能更多)声明为素数,并且仅使用不能被其中一个整除的数字。你的面具阵列。但这是一个非常先进的概念。

然而,我已经用约15行代码(也用于projecteuler)在C中写了2和3的质量分数因子分解,因此可以有效地实现这些东西;)

答案 1 :(得分:2)

最直接的改进是切换到代表奇数的位。因此,要覆盖M= 20亿个数字或10亿个赔率,您需要1000/8 = 1.25亿个字节= ~120 MB的内存(在堆上分配它们,仍然使用calloc函数) 。

位置i的位代表数字2*i+1。因此,当标记素数p的倍数时,即p^2, p^2+2p, ..., M,我们p^2=(2i+1)^2=4i^2+4i+1由位置j=(p^2-1)/2=2i(i+1)处的一位代表,以及p的下一个倍数位于p=2i+1

的位置增量上方
for( i=1; ; ++i )
  if( bit_not_set(i) )
  {
      p=i+i+1;
      k=(p-1)*(i+1);
      if( k > 1000000000) break;
      for( ; k<1000000000; k+=p)
         set_bit(k); // mark as composite
  }
// all bits i>0 where bit_not_set(i) holds, 
// represent prime numbers 2i+1

下一步是切换到适合您的缓存大小的较小。这应该加快速度。您只需要为20亿平方根的平均值保留存储区域,即44721.

首先,筛选这个较小的区域,找到那里的素数;然后将这些素数写入单独的int数组;然后使用这个素数数组来筛选每个段,可能会将找到的素数打印到stdout或其他任何部分。