【问题标题】:How can I reserve memory for a very large sieve?如何为一个非常大的筛子保留内存?
【发布时间】:2013-03-25 15:48:14
【问题描述】:

我想通过筛选多达 100,000,000 个来生成素数,但声明此范围的 bool 数组会使我的程序崩溃。 这是我的代码:

long long i,j,n;
bool prime[100000000+1];
prime[1]=prime[0]=false;
for(i=2;i<=100000000;i++){
    prime[i]=true;
}
for(i=2;i<=100000000;i++){
    if(prime[i]==false){
        continue;
    }
    for(j=i*2;j<=100000000;j+=i){
        prime[j]=false;
    }
}

我该如何解决这个问题?

【问题讨论】:

  • 堆栈大小有限制。使用malloc/newmmap动态预留内存。
  • 这是什么语言?
  • 如果您想在 C/C++ 中使用类似的东西,您可能需要使用专门的数据结构来优化内存空间的使用,即使用 1 位而不是 1 字节
  • @StanR。 no 应该是 C++ 因为有 bool 类型。
  • @IvayloStrandjev,你应该让 OP 告诉我们语言:)

标签: c++ arrays memory-management limit


【解决方案1】:

数组素数的大小为 100 MB,不允许在堆栈上声明如此大的数组。尝试将数组放在全局范围内以将其分配到堆上,或者使用 new(in C++) 或 malloc(in C) 进行分配。之后不要忘记释放内存!

【讨论】:

    【解决方案2】:

    变量可以存储在三个不同的内存区域:静态内存、自动内存、动态内存。自动内存(非静态局部变量)的大小有限,您越过了它,这会使程序崩溃。另一种方法是将您的数组标记为静态,这会将您的数组放置在静态存储中,或者使用动态内存。

    由于这是标记为 C++... 使用std::vector,使用简单,使用动态内存。

    #include <vector>
    //...
    //...
    long long i,j,n;
    std::vector<bool> prime(100000000+1, true);
    prime[1]=prime[0]=false;
    for(i=2;i<=100000000;i++){
        if(prime[i]==false){
            continue;
        }
        for(j=i*2;j<=100000000;j+=i){
            prime[j]=false;
        }
    }
    

    std::vector&lt;bool&gt; 使用“位高效”表示,这意味着这里的 std::vector 将比传统数组少大约 81 倍的内存。

    std::bitset 类似,但大小不变,您必须将其标记为静态以避免占用自动内存中的空间。

    您还没有问过,但 Erastostenes Sieve 并不是计算大量素数的最快算法。似乎Sieve of Atkin 速度更快,使用的内存更少。

    1 - 当您的系统有 8 位字节时。

    【讨论】:

    • 为什么他应该改变他的代码?你错过了解释
    【解决方案3】:

    你应该制作一个这样大小的整体筛子。相反,使用 Eratosthenes 的分段筛子在连续的段中执行筛分。在第一段,计算段内每个筛选素数的最小倍数,然后以正常方式将筛选素数的倍数标记为合数;当所有的筛选素数都用完后,该段中剩余的未标记数为素数。然后,对于下一段,每个筛分质数的最小倍数是前一段中结束筛分的倍数,所以筛分一直持续到结束。

    考虑将 100 到 200 分成 20 段的示例; 5个筛选素数分别是3、5、7、11和13。在从100到120的第一段中,bitarray有10个槽,槽0对应101,槽k对应100+ 2*k* + 1,slot 9对应119。segment中3的最小倍数是105,对应slot 2;槽 2+3=5 和 5+3=8 也是 3 的倍数。槽 2 处 5 的最小倍数是 105,槽 2+5=7 也是 5 的倍数。7 的最小倍数是 105在槽 2 处,槽 2+7=9 也是 7 的倍数。以此类推。

    函数primes接受参数lohideltalohi 必须是偶数,lo hi 和 lo 必须大于 hi 的平方根。段大小是 delta 的两倍。长度为 m 的数组 ps 包含小于 hi 平方根的筛选素数,由于偶数被忽略,因此删除了 2,由埃拉托色尼筛法。数组qs 包含对应筛分素数的当前段中最小倍数的sieve 位数组的偏移量。在每个段之后,lo 前进两倍 delta,因此对应于 sieve 位数组的索引 i 的数字是 lo + 2 i + 1.

    function primes(lo, hi, delta)
      sieve := makeArray(0..delta-1)
      ps := tail(primes(sqrt(hi)))
      m := length(ps)
      qs := makeArray(0..m-1)
      for i from 0 to m-1
        qs[i] := (-1/2 * (lo + ps[i] + 1)) % ps[i]
      while lo < hi
        for i from 0 to delta-1
          sieve[i] := True
        for i from 0 to m-1
          for j from qs[i] to delta step ps[i]
            sieve[j] := False
          qs[i] := (qs[i] - delta) % ps[i]
        for i from 0 to delta-1
          t := lo + 2*i + 1
          if sieve[i] and t < hi
            output t
        lo := lo + 2*delta
    

    对于上面给出的示例,这称为primes(100, 200, 10)。在上面给出的示例中,qs 最初为 [2,2,2,10,8],对应于最小倍数 105、105、105、121 和 117,并为第二段重置为 [1,2, 6,0,11],对应最小的倍数 123, 125, 133, 121 和 143。

    delta 的值很关键;您应该使 delta 尽可能大,只要它适合缓存内存,以提高速度。将您的语言库用于位数组,这样您只需为每个筛子位置取一个位。如果您需要一个简单的 Eratosthenes 筛来计算筛分质数,这是我最喜欢的:

    function primes(n)
      sieve := makeArray(2..n, True)
      for p from 2 to n step 1
        if sieve(p)
          output p
          for i from p * p to n step p
              sieve[i] := False
    

    您可以在my blog 看到更多涉及素数的算法。

    【讨论】:

      猜你喜欢
      • 2021-04-28
      • 2017-04-08
      • 2022-08-15
      • 2014-06-06
      • 2013-11-24
      • 1970-01-01
      • 2020-08-13
      • 2020-09-26
      • 2019-08-08
      相关资源
      最近更新 更多