【问题标题】:Prime-sieve optimized for memory针对内存优化的 Prime-sieve
【发布时间】:2015-01-08 22:15:28
【问题描述】:

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

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

我已经实现了一个筛子,它只对与 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);
}

【问题讨论】:

  • 这个问题似乎跑题了,因为它没有具体的编程问题。
  • 有代表的人。 17,000 人应该真正认识到 StackOverflow 是关于具有特定答案的特定编程问题。诸如“有什么更好的”之类的问题不适合这里的问答格式。
  • @abelenky:是的,我在第一条评论中明白了你的意思,但我要求的是针对内存优化的素筛。如果我没有表现出任何努力,那么......好吧,你知道我会得到什么样的cmets。所以我分享了我的想法,现在我要求改进。它可以是一个算法解决方案,OR - 如果有人在我的实现中发现了一些明显的东西,那么它也可以是改进的建议。无论哪种情况,无论您的声誉如何,编程问题都应该非常清楚(请原谅我没有检查它)。
  • 我相信你对CodeReview很熟悉。第一个“评论”不是故意评论。这只是投票结束的副作用。
  • @barakmanos 宏使用[n / (3*CHAR_BIT)] 索引数组,但数组大小声明为RANGE/3。不应该是RANGE/24吗?

标签: c optimization primes


【解决方案1】:

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

为了节省一点内存,你可以进入下一个级别,注意只有 8 个数字(模 30)与 30 互质。它们是 1、7、11、13、17、19、 23, 29. 使用这一事实并将其存储为位,内存减少了 30 倍。

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

【讨论】:

  • 好的,我花了一些时间来理解这个答案,因为“只有 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 个奇数数字,没有简单的公式可以将数字映射到位。
  • 好的,所以我需要再看看你的解决方案,然后试着弄清楚。
猜你喜欢
  • 1970-01-01
  • 2018-08-10
  • 1970-01-01
  • 2011-08-28
  • 2012-02-21
  • 1970-01-01
  • 2015-02-02
  • 2016-04-25
  • 1970-01-01
相关资源
最近更新 更多