Constexpr для инициализации массива

#c

Вопрос:

Я хочу запустить программу (c ), в которой я храню массив, скажем, размером 10000 из первых 10000 простых чисел, которые я вызываю int prime[10000] . Теперь либо я мог бы начать писать первые 10000 простых чисел вручную и инициализировать массив, как int prime[10000] = { 2,3,5....} , но, как вы можете себе представить, это займет некоторое время.

Вместо этого то, что я делаю прямо сейчас, это то, что у меня есть следующий код в файле prime.cpp

 const int prime_size = 10000;

int prime[prime_size];

bool is_prime(int i) {
    for (int j = 2;j < i;j  ) {
        if (i % j == 0) {
            return false;
        }
    }
    return true;
}

void init_prime() {
    int prime_counter = 1;
    prime[0] = 2;
    while (prime_counter < prime_size) {
        int counter = prime[prime_counter - 1];
        while (true) {
            counter  ;
            if (is_prime(counter)) {
                prime[prime_counter] = counter;
                prime_counter  ;
                break;
            }
        }
    }
}
 

Затем объявление функции инициализации и т.д. в prime.h

 extern int prime[];

void init_prime();
 

и, наконец, моя основная программа

 #include <iostream>

#include "prime.h"

int main() {
    init_prime();

    std::cout << prime[8] << std::endl;
}
 

Это работает абсолютно нормально и все такое, но когда я запускаю программу, инициализация простого массива занимает пару секунд. Есть ли способ инициализировать этот массив во время компиляции с помощью constexpr, чтобы мне не приходилось ждать пару секунд при каждом запуске программы?

Из того, что я прочитал, я должен иметь возможность поместить constexpr перед void init_prime() в prime.cpp потому что он может быть оценен во время компиляции. Если я это сделаю, компилятор скажет мне, что я также должен поместить constexpr перед bool is_prime(int i) .

Однако, если я это сделаю, я больше не смогу вызывать функцию в main . Это выдает мне ошибку компоновщика: «неопределенная ссылка на `init_prime()'». Не имеет значения, помещаю ли я constexpr перед объявлением init_prime() в заголовочном файле. Он по-прежнему выдает мне ту же ошибку компоновщика.

Если constexpr не подходит для использования, то есть ли другой способ инициализировать этот массив во время компиляции?

Если я помещу все в main.cpp файл, затем это работает, но я не получаю ускорения, и это все равно занимает пару секунд. Я использую mingw64 для компиляции, если это имеет значение, и я компилирую без оптимизации.

Ответ №1:

Вместо C-array используйте std::array :

 constexpr int prime_size = 10000;

constexpr bool is_prime(int i) {
    for (int j = 2;j < i;j  ) {
        if (i % j == 0) {
            return false;
        }
    }
    return true;
}

constexpr std::array<int, prime_size> init_primes() {
    std::array<int, prime_size> primes{};
    int prime_counter = 1;
    primes[0] = 2;
    while (prime_counter < prime_size) {
        int counter = primes[prime_counter - 1];
        while (true) {
            counter  ;
            if (is_prime(counter)) {
                primes[prime_counter] = counter;
                prime_counter  ;
                break;
            }
        }
    }
    return primes;
}

constexpr std::array<int, prime_size> primes = init_primes();
 

ДЕМОНСТРАЦИЯ

Ответ №2:

Вам повезло, так как я реализовал что-то вроде около двух лет назад, используя решето Эратосфена:

 template<size_t N>
class EratostenesSive
{
public:
    constexpr EratostenesSive()
        : oddPrimes{}
    {
        std::fill(oddPrimes.begin(), oddPrimes.end(), true);
        oddPrimes[0] = false;
        
        for (size_t p = 3; p * p <= N; p  = 2) {
            if (oddPrimes[p/2]) {
                for (size_t i = p * p; i <= N; i  = 2 * p) {
                    oddPrimes[i/2] = false;
                }
            }
        }
    }
    
    bool isPrime(size_t x) const
    {
        if (x < 2) return false;
        if (x == 2) return true;
        if (x > max()) throw std::invalid_argument("Sive limit reached");
        return x%2 != 0 amp;amp; oddPrimes[x/2];
    }
    
    bool isPrimeInRange(size_t x) const
    {
        return x <= max() amp;amp; isPrime(x);
    }
    
    std::optional<size_t> next(size_t x) const
    {
        auto index = (x   1) / 2;
        while (index < oddPrimes.size() amp;amp; !oddPrimes[index])   index;
        if (index < oddPrimes.size()) return index * 2   1;
        return {};
    }
    
    constexpr size_t max() const {
        return N | 1;
    }
    
private:
    std::array<bool, (N   1) / 2> oddPrimes;
};
 

https://wandbox.org/permlink/enGXi6bRUrgGicE2

Ответ №3:

У вас уже есть код для генерации простых чисел. Вы можете вывести их в файл primes.txt , разделенный запятой. Затем включите этот файл в свою программу для инициализации массива.

Т.е. генерировать primes.txt (с таким количеством простых чисел, как вам нравится):

 2, 3, 5, 7, 11
 

Затем включите этот файл для инициализации массива:

 unsigned primes[] =
{
    #include "primes.txt"
};
 

Комментарии:

1. Я должен был добавить, что я не хочу этого, потому что то, что я на самом деле хочу использовать, имеет размер около 5 миллионов, и было бы слишком много данных для хранения.

2. @user1724871284 OK. Я по-прежнему буду публиковать ответ, поскольку считаю, что это самое простое решение вопроса, как указано. Если вы не хотите сохранять файл, вы можете рассмотреть возможность создания его как временного файла в процессе сборки.

3. «и было бы слишком много данных для хранения» -> но выполнение этого constexpr должно привести к более или менее тому же результату — за исключением того, что в этом случае не будет отдельного файла, «только» исполняемый файл будет больше (простые числа все равно нужно где -то хранить, верно?)