Weather Vane がコメントで述べたように、2 を除くすべての偶数は非素数であるため、1 つおきの数のみを考慮することで追加のスペースを節約できます。
したがって、ビット配列では、各ビットは奇数を表します。
これは、この手法を使用して数年前に行った実装です。
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <time.h>
#include <math.h>
#include <stdint.h>
uint8_t *num;
int count = 0;
FILE *primefile;
int main(int argc, char *argv[])
{
int i,j,root;
time_t t;
if (argc>1) count=atoi(argv[1]);
if (count < 100) {
fprintf(stderr,"Invalid number\n");
exit(1);
}
if ((num=calloc(count/16,1))==NULL) {
perror("calloc failed");
exit(1);
}
if ((primefile=fopen("primes.dat","w"))==NULL) {
perror("Coundn't open primes.dat");
exit(1);
}
t=time(NULL);
printf("Start:\t%s",ctime(&t));
root=floor(sqrt(count));
// write 2 to the output file
i=2;
if (fwrite(&i,sizeof(i),1,primefile)==0) {
perror("Couldn't write to primes.dat");
}
// process larger numbers
for (i=3;i<count;i+=2) {
if ((num[i>>4] & (1<<((i>>1)&7)))!=0) continue;
if (fwrite(&i,sizeof(i),1,primefile)==0) {
perror("Couldn't write to primes.dat");
}
if (i<root) {
for (j=3*i;j<count;j+=2*i) {
num[j>>4]|=(1<<((j>>1)&7));
}
}
}
t=time(NULL);
printf("End:\t%s",ctime(&t));
fclose(primefile);
return 0;
}
これnum
は、検索の上限に基づいて動的に割り当てられるビット配列です。したがって、1000000000 (10 億) までのすべての素数を検索すると、64000000 (6400 万) バイトのメモリが使用されます。
主な表現は次のとおりです。
「通常の」ビット配列の場合:
セットビットi
:
num[i>>3] |= (1<<(i&7);
// same as num[i/8] |= (1<<((i%8));
チェックビットi
:
(num[i>>3] & (1<<(i&7))) != 0
// same as (num[i/8] & (1<<(i%8))) != 0
他のすべての数値を追跡しているだけなのでi
、2 で割ります (または同等に、右に 1 シフトします:
num[i>>4] |= (1<<((i>>1)&7);
// same as num[(i/2)/8] |= (1<<(((i/2)%8));
(num[i>>4] & (1<<((i>>1)&7))) != 0
// same as (num[(i/2)/8] & (1<<((i/2)%8))) != 0
上記のコードには、除算と 2 の累乗によるモジュラスがビット シフトとビットごとの AND マスクに置き換えられるマイクロ最適化がいくつかありますが、ほとんどのコンパイラはそれを行う必要があります。