【问题标题】:How to use less memory in Sieve_of_Eratosthenes如何在 Sieve_of_Eratosthenes 中使用更少的内存
【发布时间】:2019-08-17 02:02:08
【问题描述】:

我正在一个类似 leetcode 的平台上编码。有一个任务:计算低于给定界限的素数。

我使用了算法:https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes

我从这里复制代码:https://www.geeksforgeeks.org/sieve-of-eratosthenes/,除了我让false 代表isPrime 以避免使用memset。这是我的代码:

void SieveOfEratosthenes(int n) 
{ 
    bool *prime = new bool[n+1]();  // initialized by false by default  

    for (int p=2; p*p<=n; p++) 
    { 
        if (prime[p] == false) 
        { 
            for (int i=p*p; i<=n; i += p) 
                prime[i] = true; 
        } 
    } 

    for (int p=2; p<=n; p++) 
       if (prime[p]) 
          cout << p << " "; 
}

但是,当我执行它时,平台告诉我在100 000 000作为输入的情况下我使用了太多的内存。

我检查了sizeof(bool) 等于1

有什么方法可以减少这段代码的内存消耗吗?

【问题讨论】:

  • @JamesKPolk 好吧,我想我在那篇文章中已经使用了所有方法。
  • std::vector&lt;bool&gt; 每个元素使用 1 个 bit,因此使用 bool 数组的 1/8 存储空间。
  • 您应该尝试实施 Eratosthenes 的分段筛吗?它的主要目标是减少内存使用。
  • @JosephWood ... 从而大大加快了速度,让内存适合缓存。 :)

标签: c++ memory profiling primes


【解决方案1】:

几个建议:

  • 使用仅表示奇数的位数组
  • 将问题分成几段,这样部分筛子使用的内存要少得多

@Kim Walish 在这里有一个快速的 C++ 版本:

https://github.com/kimwalisch/primesieve/wiki/Segmented-sieve-of-Eratosthenes

您可以通过始终将段大小限制为 L1 缓存大小以及将 IsPrime 数组更改为奇数位数组来使其使用更少的内存。

【讨论】:

    【解决方案2】:

    这是一个内存优化的 Eratosthenes 筛子的实现。基本思想是,您只需要存储奇数的状态。其余部分与正常实现类似。

    #include <iostream>
    
    class Solution {
    public:
        int countPrimes(int n) {
            //if(n <= 1) return 0; // including n
            if(n <= 2) return 0; // number of primes less than 0 / 1 / 2 is 0
            const int MAXN = 1500000 + 5; // adjust MAXN accordingly
            // finding prime from 1 up to N
            int status[(MAXN >> 1) + 1]; // we need space for only the odd numbers
            // works well up to 1.5 * 10 ^ 6, for numbers larger than that, you need to adjust the second operand accordingly
            int prime[115000 + 1000]; // prime number distribution , pi(x) = x/ (ln(x) - 1) , adjust this according to MAXN
    
            // If status[i] = 0 -> i is prime
            // If status[i] = 1 -> i is not prime
    
            for(int i = 1 ; i <= (n >> 1) ; ++i) status[i] = 0; // for every i , 2 * i + 1 is the odd number, marking it as prime
    
            int sqrtN = static_cast <int> ((sqrt (static_cast <double> (n))));
            // computing sqrt(N) only once because it is costly computing it inside a loop
    
            // only accounting the odd numbers and their multiples
            for(int i = 3 ; i <= sqrtN ; i += 2){
                if(status[i >> 1] == 0){
                    // if this is still a prime then discard its multiples
                    // first multiple that needs to be discarded starts at i * i
                    // all the previous ones have already been discarded
                    for(int j = i * i ; j <= n ; j += (i + i)) {
                        //printf("Marking %d as not prime\n",j);
                        status[j >> 1] = 1;
                    }
                }
            }
            int counter = 0;
            prime[counter++] = 2;
            for(int i = 3 ; i <= n ; i += 2){
                if(status[i >> 1] == 0){
                    prime[counter++] = i;
                }
            }
    
            if( (n & 1) && !status[n >> 1]) counter--; // if n is prime, discard n
    
            std::cout << "Number of primes less than " << n << " is " << counter << "\n";
            for(int i = 0 ; i < counter; ++i){
                std::cout << prime[i];
                if(i != counter - 1) std::cout << "\n";
            }
            std::cout << "\n";
    
            return counter;
        }
    };
    
    int main(int argc, char const *argv[])
    {
        Solution solution;
        int n; std::cin >> n; 
        solution.countPrimes(n);
        return 0;
    }
    

    【讨论】:

      猜你喜欢
      • 2017-03-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-09-02
      • 2021-10-09
      • 2011-01-28
      • 1970-01-01
      相关资源
      最近更新 更多