C 为内存优化的素筛

C 为内存优化的素筛,c,optimization,primes,C,Optimization,Primes,我正在寻找一个在内存消耗方面高效的prime sieve实现 当然,素性测试本身应该以恒定的和最小操作数执行 我已经实现了一个筛子,它只指示与6的倍数相邻的数字的素性 对于任何其他数字,它要么是2或3(因此为素数),要么是2或3的倍数(因此为非素数) 这就是我想到的,我一直在想,在这些要求下是否有更好的东西: 界面: #include <limits.h> // Defined by the user (must be less than 'UINT_MAX') #define R

我正在寻找一个在内存消耗方面高效的prime sieve实现

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

我已经实现了一个筛子,它只指示与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);
}
#包括
//由用户定义(必须小于“UINT_MAX”)
#定义范围4000000000
//主筛阵列所需的实际长度
#定义ARR_LEN(((范围-1)/(3*字符位)+1))
//假设“sieve”中的所有条目都初始化为零
void Init(字符筛[ARR_LEN]);
//假设已调用“Init(sieve)”,且“1=UINT\u MAX
#错误范围超出限制
#恩迪夫
实施:

#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);
}
#包括
#定义GET_位(sieve,n)((sieve[(n)/(3*CHAR_位)]>>((n)%(3*CHAR_位)/3))&1)

#定义SET_BIT(sieve,n)sieve[(n)/(3*CHAR_BIT)]|=1您已经正确地推断出,您可以利用这样一个事实:只有两个数字相对素数为6,即1和5(aka+1和-1)。利用这一事实并将筛子存储为位而不是字节,可以将内存需求减少24倍

为了节省更多的内存,您可以转到下一个级别,注意只有8个数(模30)相对素数为30。他们是1,7,11,13,17,19,23,29。利用这一事实并存储为位,内存减少了30倍


实现说明:筛中的每个字节表示8个相对素数为30的倍数。例如,
sieve[3]
中包含的位表示数字
91、97、101、

这个问题似乎离题了,因为它没有特定的编程问题。代表17000的人应该真正认识到StackOverflow是关于具有特定答案的特定编程问题。像“还有更好的吗”这样的问题不适合这里的问答格式。@abelenky:是的,我在第一条评论中就明白了你的意思,但我要求的是一个优化内存的素数筛。如果我没有表现出任何努力,那么。。。你知道我会得到什么样的评论。所以我分享了我的想法,现在我要求改进。它可以是一个算法解决方案,或者——如果有人在我的实现中发现了一些明显的东西,那么它也可以是一个改进建议。在任何一种情况下,编程问题都应该非常清楚,无论你的名声如何(请原谅我没有检查它)。我相信你很熟悉。第一个“评论”不是故意的评论。这只是投票关闭的副作用。@barakmanos宏用
[n/(3*CHAR\u BIT)]
索引数组,但数组大小声明为
范围/3
。这不应该是
RANGE/24
?好吧,我花了一些时间才理解这个答案,因为“只有8个数字相对30素数”的说法不太正确(例如-31、37等)。你的意思是,在30个数字的每个部分中,最多有8个素数,另外,我需要检查2,3和5的可除性。。。对吗?@barakmanos是的,没错。我建议的是一个包含30个条目的查找表,其中表中的每个条目要么是0(对于那些是2、3或5的倍数的数字),要么是8个可能是素数的数字的位掩码。因此,给定一个数字
N
,您可以计算
N%30
。如果表中对应的条目为0,则
N
是2、3或5的倍数。否则,您就有一个小掩码可用于
sieve[N/30]
。哦,这肯定不同于我最初的方法。我的数组中没有2或3的倍数的数字的条目(我不使用筛子检查它们)。@barakmanos是的,在您的情况下,将数字映射到位更容易,因为6个数字的每个部分中只有两个数字。在30个数字的每一部分中有8个奇怪的数字,没有简单的公式可以将数字映射到位。好的,所以我需要再看一下你的解决方案,然后试着找出它。