【问题标题】:Sieve of Eratosthenes埃拉托色尼筛
【发布时间】:2011-12-16 19:30:33
【问题描述】:

我在解决Project Euler 上的问题时阅读了 Eratosthenes 的筛子。我相信你们知道我在说哪个问题。 事情就是这样。我的代码设法正确显示了 100 万以下的所有素数。 但是,当我为 200 万个尝试相同的实现时,它给了我一个分段错误...... 我对错误出现的原因有一定的了解,但不知道如何纠正它...... 这是 100 万以下素数的代码。

#include<stdio.h>
int main(void)
{
   int i,k=2;
   int j;
   int n=1000000;
   int prime[2000000]={};
   for(i=0;i<n;i++) // initializes the prime number array
   {
      prime[i]=i;
   }
   for(i=2;i<n;i++) // Implementation of the Sieve
   {
      if(prime[i]!=0)
      { 
         for(j=2;j<n;j++)
         {
            {
               prime[j*prime[i]]=0;
               if(prime[i]*j>n)
                  break;    
            }
         }
      }
   }
   for(i=0;i<n;i++) // Prints the prime numbers
      if(prime[i]!=0)
      {
         printf("%d\n"prime[i]);
      }
      return(0);
   }
}

【问题讨论】:

  • 你把int n=1000000;改成int n=2000000;了吗
  • 这看起来像是一个可能越界的数组访问:prime[j*prime[i]]=0
  • 附带说明,您可能应该使用int 以外的其他数据类型。不保证 Int 是任何特定大小,除了 16 位。作为一个风格问题,我建议long 用于 32k 以上的数字。
  • 如果他要索引一个大数组,他不妨使用size_t

标签: c sieve-of-eratosthenes sieve


【解决方案1】:

埃拉托色尼筛法的简单实现

方法: 我创建了一个大小为 n+1 的布尔向量(比如 n=9,然后是 0 到 9),它在所有地方都成立。现在,for i=2 将所有 2 的倍数的位置标记为 false(如 n=9 时的 4,6 和 8) .对于 i=3,将所有 3 的倍数的位置标记为 false(如 n=9 时的 6 和 9)。现在,对于 i=4 条件 i*i 是 false 因为 4*4 =16 > 9。所以,现在打印所有真正有价值的地方。

void sieve(int n)
{
vector<bool> isPrime(n+1,true);
for(int i=2;i*i<=n;i++){
    if(isPrime[i])
    {
       for(int j=2*i;j<=n;j=j+i)
           isPrime[j]=false;
     }
  }
 for(int i=2;i<=n;i++){
    if(isPrime[i])
        cout<<i<<" ";
  }
}

【讨论】:

  • 这个答案只是芝士头和威廉华纳答案的变体。它没有回答有关分段错误的问题
【解决方案2】:

这是我的实现(Java) 简单得多,因为你真的只需要一个数组,只需从 2 开始 for 循环。

编辑:@cheesehead 的解决方案可能更好,我刚刚阅读了筛子的描述,并认为这将是一个很好的思考练习。

      // set max;
      int max = 100000000;

      // logic
      boolean[] marked = new boolean[max]; // all start as false
      for (int k = 2; k < max;) {
         for (int g = k * 2; g < max; g += k) {
            marked[g] = true;
         }
         k++;
         while (k < max && marked[k]) {
            k++;
         }
      }

      //print
      for (int k = 2; k < max; k++) {
         if (!marked[k]) {
            System.out.println(k);
         }
      }

【讨论】:

    【解决方案3】:

    这是我的实现。

    #include <stdio.h>
    #include <math.h>
    #include <stdlib.h>
    
    int* sieve(int n) {
      int* A = calloc(n, sizeof(int));
      for(int i = 2; i < (int) sqrt(n); i++) {
        if (!A[i]) {
          for (int j = i*i; j < n; j+=i) {
            A[j] = 1;
          }
        }
      }
      return A;
    }
    

    我在 i5 Kaby Lake 上对前 1,000,000,000 个数字进行了基准测试。

    ? time ./sieve 1000000000
    ./sieve 1000000000  16.21s user 1.05s system 99% cpu 17.434 total
    

    我只是从维基百科翻译了this 伪代码。

    【讨论】:

      【解决方案4】:

      您正在堆栈中分配一个巨大的数组:

      int prime[2000000]={};
      

      4 字节乘以 200 万等于 8 兆字节,这通常是最大堆栈大小。分配超过这个值会导致分段错误。

      您应该在堆中分配数组,而不是:

      int *prime;
      prime = malloc(2000000 * sizeof(int));
      if(!prime) {
          /* not enough memory */
      }
      /* ... use prime ... */
      free(prime);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-05-31
        • 2016-10-29
        相关资源
        最近更新 更多