【问题标题】:Reducing memory usage when designing a sieve of eratosthenes in C在 C 中设计 Eratosthenes 筛时减少内存使用
【发布时间】:2013-11-26 21:56:07
【问题描述】:

我正在尝试在 C 中设计一个 Eratosthenes 筛子,但我遇到了两个我无法弄清楚的奇怪问题。这是我的基本程序大纲。要求用户设置显示素数的范围。如果范围最小值小于 9,则将最小值设置为 9。用范围内的所有奇数填充一个数组。

1) 我试图通过像这样声明可变大小的数组来减少内存使用:

    if (max<=UINT_MAX)
       unsigned int range[(max-min)/2];
    else if (max<=ULONG_MAX)
         unsigned long int range[(max-min)/2];
    else if (max<=ULLONG_MAX)
         unsigned long long int range[(max-min)/2];

为什么不编译?变量 min 和 max 之前被声明为 ints 并且包括 limits.h。我已经注释掉了选择结构,现在只声明了unsigned long long int range[(max-min)/2];,它现在可以编译和工作。

2) 我的代码可以运行,但有时会将小素数标记为非素数。

#include<stdio.h>
#include<limits.h>

void prime(int min, int max)
{
    int i, f=0;
    //declare variable size array
    /*if (max<=(int)UINT_MAX)
       unsigned int range[(max-min)/2];
   else if (max<=(int)ULONG_MAX)
         unsigned long int range[(max-min)/2];
    else if (max<=(int)ULLONG_MAX)*/
         unsigned long long int range[(max-min)/2];
    //fill array with all odd numbers
    if (min%2==0)
    {
        for (i=min+1;i<=max;i+=2)
        {
            range[f]=i;
            f+=1;
        }
    }
    else
    {
        for (i=min;i<=max;i+=2)
        {
            range[f]=i;
            f+=1;
        }
    }
    //assign 0 to cell if divisible by any number other than itself
    for (i=3;i<=sqrt(max);++i)
    {
        for (f=0;f<=((max-min)/2);f++)
        {
            if (range[f]%i==0 && f!=i)
                range[f]=0;
        }
    }
    //troubleshoot only: print full range
    for (f=0;f<=((max-min)/2);f++)
    {
            printf("ALL: %d / %d\n", f, range[f]);
    }    
    //display all primes
    if (min==9) /*print primes lower than 9 for ranges where min<9*/
            printf("2\n3\n5\n7\n");
    for (f=0;f<=((max-min)/2);f++) /*print non 0 numbers in array*/
    {
        if (range[f]!=0)
            printf("%d\n", range[f]);
    }
}
int main(void)
{
    int digits1, digits2;
    printf("\n\n\nCalculate Prime Numbers\n");
    printf("This program will display all prime numbers in a given range. \nPlease set the range.\n");
    printf("Minimum: ");
    scanf("%d", &digits1);
    if (digits1<9)
       digits1=9;
    printf("Maximum: ");
    scanf("%d", &digits2);
    printf("Calculating...");
    printf("All prime numbers between %d and %d are:\n", digits1, digits2);
    prime(digits1, digits2);
    getchar();
    getchar();
}

例如,如果 digits=1 和 digits2=200 我的程序输出 1 和 200 之间的所有素数,但 11 和 13 除外。11 和 13 被筛选出来,我不明白为什么这种情况会发生在越来越多的低数字上随着digits2的增加。

3) 最后,我的筛子是否是合适的埃拉托色尼筛子?它有点工作,但我觉得有一种更有效的方法来筛选非素数,但我不知道如何实现它。我对这个程序的目标之一是尽可能高效。同样,我现在拥有的是:

    //assign 0 to cell if divisible by any number other than itself
    for (i=3;i<=sqrt(max);++i)
    {
        for (f=0;f<=((max-min)/2);f++)
        {
            if (range[f]%i==0 && f!=i)
                range[f]=0;
        }
    }

感谢您阅读所有内容!很抱歉再次发布与eratosthenes 相关的问题,并提前感谢您的帮助!

【问题讨论】:

  • 1.您不能像您尝试做的那样对单个变量进行多个声明;根本不允许。为了进一步减少内存使用,将数组视为位图,并且只记录图中的奇数。

标签: c primes sieve-of-eratosthenes


【解决方案1】:

不,它不是适当的埃拉托色尼筛。 Eratosthenes 算法的筛子不涉及余数测试,Wikipedia is real clear on this 我认为。 :) 重点是避免试用分裂,免费获得素数,无需测试。

怎么样?通过生成它们的倍数,从我们识别的每个素数中,按升序一个接一个。

素数p的倍数是:2p, 2p + p, 2p + p + p, ...

素数p的奇数倍数是:3p, 3p + 2p, 3p + 2p + 2p, ...

当我们枚举它们时,我们将它们标记在筛子数组中。有些会被标记两次或更多,例如15 将被标记为 3 和 5(因为 3 * 5 == 5 * 3)。因此,我们可以从p2开始枚举和标记:

  for( i=3; i*i < n; i += 2 )
      if( !sieve[i] )         // if `i` is not marked as composite
          for( j = i*i; j < n; j += 2*i )
          {
              sieve[j] = 1;   // 1 for composite, initially all are 0s
          }

筛子的关键是:我们不在数组中存储数字。它不是INTs 的数组;它是一个 1 位标志的数组,值为 0 或 1。筛子数组中条目的 索引 表示筛子保持其状态的数字:已标记,即复合,或尚未标记,即可能是素数。

所以最后,所有未标记的条目都表示素数。当然,您将需要设计一个寻址方案,例如索引i 处的条目可能对应于数字a + 2*i,其中a 是范围的奇数开始。由于您的范围从某个偏移量开始,因此该方案称为offset sieve of Eratosthenes。一个骨架 C 实现是here

为了最大限度地减少内存使用,我们需要将我们的数组视为一个位数组。在 C++ 中,例如这很简单:我们将它声明为vector&lt;bool&gt;,它会自动为我们打包。在 C 中,我们必须自己做一些打包和解包。

忠告:不要吝啬临时变量。命名程序中每个有意义的实体。您的代码中不应有任何(max-min)/2;而是定义 width = max - min 并使用该名称。将小的优化留给编译器。 :)


你的第一个问题:这是一个范围的东西。你的代码相当于

if (max<=UINT_MAX)
    {   unsigned int range[(max-min)/2]; }    // note the curly braces!
else if (max<=ULONG_MAX)
    {   unsigned long int range[(max-min)/2]; }
else if (max<=ULLONG_MAX)
    {   unsigned long long int range[(max-min)/2]; }

所以这里有三个range 数组声明,每个声明都在自己的范围内,在相应的块内。每个都在进入其封闭块({)时创建,并在退出时销毁(})。换句话说,它不再存在于您的 prime 函数的其余部分。实际上,这意味着如果您在 if 块内声明变量,则只能在该块内使用它(在相应的大括号 {} 之间)。

【讨论】:

    【解决方案2】:

    Q1:你不能在同一个作用域内声明一个符号(这里:range)两次。这不完全是您的问题,但您正在尝试这样做:您在 if 范围内声明 range 并且它在外部不可见。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-10-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多