使用位掩码使用Sieve的方法列出素数

时间:2013-01-08 17:43:10

标签: c arrays primes bitmask sieve-of-eratosthenes

我编写了以下代码,使用Sieve方法列出所有素数高达20亿。我使用bitmasking进行标记。虽然我能够正确地得到素数,但每次都会缺少一些素数。请帮我找一下程序中的错误。

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

#define MAX 2000000000

char* listPrimes(){
int block = sqrt(MAX);
char* mark = calloc((MAX/8),sizeof(char));
int i = 2;
int j;
char mask[8];
for(j=0;j<8;j++)
    mask[j] = 0;
mask[7] = 1;
mask[6] |= mask[7] << 1;
mask[5] |= mask[7] << 2;
mask[4] |= mask[7] << 3;
mask[3] |= mask[7] << 4;
mask[2] |= mask[7] << 5;
mask[1] |= mask[7] << 6;
mask[0] |= mask[7] << 7;

for(j=0;j<8;j++)
    printf("%d ",mask[j]);
mark[0] |= mask[0];
mark[0] |= mask[1];

while (i < block){

        for (j = 2; i*j <= block; j++)
                mark[(i*j) / 8] |= mask[((i*j) % 8 )];
        i++;
    }
printf("\n");
printf("The block size is\t:\t%d\n",block);


j = 2;
while(j<=block){
    if((mark[j / 8] & mask[j]) == 0 ){
        for(i = 2;i <= MAX; i++){
            if((i%j) == 0){
                mark[i / 8] |= mask[(i % 8)];
            }
        }
    }
while((mark[++j / 8] & mask[j % 8]) != 0);
}


for(j=0;j<=MAX;j++)
        if((mark[j / 8] & mask[(j % 8)]) == 0)
            printf("%d\n", ((8*(j / 8)) + (j % 8)));

return mark;
}   

int main(int argc,char* argv[]){

listPrimes();

return 0;
}

3 个答案:

答案 0 :(得分:1)

更改中间循环以添加模数:

j = 2;
while(j<=block){
    if((mark[j / 8] & mask[j % 8]) == 0 ){
        for(i = 2;i <= MAX; i++){
            if((i%j) == 0){
                mark[i / 8] |= mask[(i % 8)];
            }
        }
    }
}

答案 1 :(得分:1)

在第二个while循环中,您从2开始循环i,然后执行if (i%j == 0)。这对于我来说也是如此。你需要检查(i!= j)。也是如上所述的模数。因此它变成: if ((i%j == 0) { if (i!=j) mark[i/j] |= mask[i%j]; }

答案 2 :(得分:1)

正如ArunMK所述,在第二个while循环中,您将素数j本身标记为j的倍数。正如Lee Meador所述,您需要为j索引取mask模8的模数,否则您将访问越界并调用未定义的行为。

您调用未定义行为的另一点是

while((mark[++j / 8] & mask[j % 8]) != 0);

您可以在不使用序列点的情况下使用和修改j。您可以通过编写

来避免这种情况
do {
    ++j;
}while((mark[j/8] & mask[j%8]) != 0);

或者,如果你坚持使用空身<{1}}循环

while

您可以使用逗号运算符。

通过访问未在

中分配的while(++j, (mark[j/8] & mask[j%8]) != 0); 来获得更多未定义的行为
mark[MAX/8]

for(i = 2;i <= MAX; i++){

此外,如果for(j=0;j<=MAX;j++) 已签名且为8位宽,

char

是实现定义的(并且可能引发实现​​定义的信号),因为

的结果
mask[0] |= mask[7] << 7;

mask[0] | (mask[7] << 7) 128)无法表示为int

但为什么要将每个数字除以不超过第二个char循环中边界的平方根的所有素数?

while

这使得你的算法不是Eratosthenes的筛子,而是试验师。

为什么不使用第一个 for(i = 2;i <= MAX; i++){ if((i%j) == 0){ 循环中的技术呢? (然后,为什么两个循环呢?)

while

不会溢出(对于给定的while (i <= block){ if ((mark[i/8] & mask[i%8]) == 0) { for (j = 2; i*j < MAX; j++) { mark[(i*j) / 8] |= mask[((i*j) % 8 )]; } } i++; } 值,如果它可以表示为MAX),并且生成正确的输出数量级更快。