如何在 Sieve_of_Eratosthenes 中使用更少的内存

How to use less memory in Sieve_of_Eratosthenes

我正在一个类似 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

有什么方法可以减少这段代码的内存使用量吗?

这是埃拉托色尼筛法的内存优化实现。基本思想是,你只需要存储奇数的状态。其余部分与正常实施类似。

#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;
}

一些建议:

  • 使用仅表示奇数的位数组
  • 将问题分解成多个部分,以便部分筛选使用更少的内存

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

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

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