Prime-sieve针对内存进行了优化

时间:2015-01-08 22:15:28

标签: c optimization primes

我正在寻找一种在内存消耗方面有效的主筛实现。

当然,素性测试本身应该以恒定的最小数量的操作执行。

我已经实施了一个筛子,只显示与6的倍数相邻的数字的原始性。

对于任何其他数字,要么是2或3(因此是素数),要么它是2或3的倍数(因此是非素数)。

所以这就是我想出来的,我一直想知道在这些要求下是否还有更好的东西:

接口

#include <limits.h>

// Defined by the user (must be less than 'UINT_MAX')
#define RANGE 4000000000

// The actual length required for the prime-sieve array
#define ARR_LEN (((RANGE-1)/(3*CHAR_BIT)+1))

// Assumes that all entries in 'sieve' are initialized to zero
void Init(char sieve[ARR_LEN]);

// Assumes that 'Init(sieve)' has been called and that '1 < n < RANGE'
int IsPrime(char sieve[ARR_LEN],unsigned int n);

#if RANGE >= UINT_MAX
    #error RANGE exceeds the limit
#endif

实施

#include <math.h>

#define GET_BIT(sieve,n) ((sieve[(n)/(3*CHAR_BIT)]>>((n)%(3*CHAR_BIT)/3))&1)
#define SET_BIT(sieve,n) sieve[(n)/(3*CHAR_BIT)] |= 1<<((n)%(3*CHAR_BIT)/3)

static void InitOne(char sieve[ARR_LEN],int d)
{
    unsigned int i,j;
    unsigned int root = (unsigned int)sqrt((double)RANGE);

    for (i=6+d; i<=root; i+=6)
    {
        if (GET_BIT(sieve,i) == 0)
        {
            for (j=6*i; j<RANGE; j+=6*i)
            {
                SET_BIT(sieve,j-i);
                SET_BIT(sieve,j+i);
            }
        }
    }
}

void Init(char sieve[ARR_LEN])
{
    InitOne(sieve,-1);
    InitOne(sieve,+1);
}

int IsPrime(char sieve[ARR_LEN],unsigned int n)
{
    return n == 2 || n == 3 || (n%2 != 0 && n%3 != 0 && GET_BIT(sieve,n) == 0);
}

1 个答案:

答案 0 :(得分:2)

您已经正确地推断出您可以利用这样一个事实,即只有两个相对于6的数字,即1和5(又名+1和-1)。使用该事实并将筛子存储为位而不是字节,可以将内存需求减少24倍。

为了节省更多的记忆,你可以进入下一个级别,并注意到只有8个数字(模30)相对于30的素数。它们分别是1,7,11,13,17,19,使用该事实并存储为比特,存储器减少了30倍。

实施说明:筛子中的每个字节代表相对于30的某个倍数的8个数字。例如,sieve[3]中包含的位代表数字91, 97, 101, ...

相关问题