Генерировать случайные числа в С++ во время компиляции

Я пытаюсь предварительно вычислить случайные значения, используя библиотеку С++ 11 random во время компиляции. Я в основном следую примерам. Что я здесь делаю неправильно?

using namespace std;
#include <iostream>
#include <vector>
#include <random>

vector<double> rands;
typedef std::mt19937_64 RNG;
uint64_t seed_val;
RNG rng; 

void initialize() {
     rng.seed(seed_val);
}

constexpr vector<double> generate_random( )                 //size_t numbers)
{   
    int numbers = 1000;
    std::uniform_real_distribution<double> zero_one(0.0, 1.0);
        for (unsigned int i = 0; i < numbers; i++) { 
             double rand_num = zero_one(rng);
             rands.push_back( rand_num );
    }
    return rands;
}

int main()
{
    cout << "TMP rands";
    for_each( rands.begin(), rands.end(), [] (double value)
    {
        cout<<value<<endl;
    });
}

Здесь примерный генератор случайных чисел компиляции, бесстыдно украденный из здесь, но подумал, что это может быть полезно для всех, кто смотрит это:

template<u32 S, u32 A = 16807UL, u32 C = 0UL, u32 M = (1UL<<31)-1>
struct LinearGenerator {
    static const u32 state = ((u64)S * A + C) % M;
    static const u32 value = state;
    typedef LinearGenerator<state> next;
    struct Split { // Leapfrog
        typedef LinearGenerator< state, A*A, 0, M> Gen1;
        typedef LinearGenerator<next::state, A*A, 0, M> Gen2;
    };
};

Ответ 1

Только функции constexpr и постоянные выражения могут быть оценены во время компиляции. Это исключает <chrono> и <random>.

Что вы можете сделать, это получить доступ к макросу препроцессора __TIME__ и определить собственный PRNG, состоящий из однострочных, constexpr функций.

Ответ 2

Не только system_clock::now() не умеет компилировать-время, но ваша функция помечена как возвращающая bool, но где-либо нет оператора возврата.

Ответ 3

Я бы попытался вытащить его из внешнего источника. Очень простой пример - скомпилировать вашу программу с определенными макропеременными в команде компиляции. Здесь $RANDOM - специальная встроенная переменная в системах unix/linux, которая автоматически возвращает случайное 16-разрядное число.

g++ -D__RANDOM__=$RANDOM yourprog.cpp -o yourprog

//yourprog.cpp
#include <iostream>
int main() {
  std::cout << "Random variable " << __RANDOM__ << std::endl;
  return 0;
}

Вы также можете написать свой собственный script или исполняемый файл для назначения своей макропеременной.

//DevRandomGenerator.cpp
#include <iostream>
#include <fstream>

class DevRandom {
private:
    std::ifstream stream;
public:

    DevRandom() {
        stream.open("/dev/urandom",std::ios::in|std::ios::binary);
    }

    unsigned int unsignedInt() {
        unsigned int u = 0;
        stream.read((char*)&u, sizeof(unsigned int));
        return u;
    }
};

int main() {
  DevRandom rand;
  std::cout << rand.unsignedInt() << std::endl;
  return 0;
}

затем скомпилируйте как:

g++ DevRandomGenerator.cpp -o DevRandomGenerator
g++ -D__RANDOM__="$(./DevRandomGenerator)" yourprog.cpp -o yourprog

Лучшим случайным генератором было бы написать программу, которая использует аудио и визуальные входы.

Ответ 5

Я знаю, что этот вопрос пять лет, и уже имеет принятый ответ. Тем не менее, я хотел бы добавить, что, возможно, возможно генерировать случайные числа во время компиляции, при том понимании, что каждый раз, когда вы запускаете программу, вы получаете одну и ту же последовательность случайных чисел. Проще говоря, если семя известно во время компиляции, компилятор разрешен, чтобы выяснить, какие случайные числа будут выводиться, и просто превратить программу в "вывод этой последовательности чисел".

Составители будут иметь ограничения на то, насколько агрессивно они оптимизируются, поэтому я не могу обещать, что они всегда будут делать эту замену, и я сомневаюсь, что любой компилятор сможет сделать замену чем-то сложным, как Mersenne Twister, но что-то более простой, чем linear_congruential_engine, имеет шанс (также единственный способ убедиться, что это произойдет, - это получить код сборки сборки компилятора, а затем посмотреть на код сборки).

Я знаю, что это возможно, потому что я реализовал случайный генератор, смоделированный после random_device, который использовал алгоритм Marsaglia Xorshift. Поскольку документ Marsaglia фактически включал множественные связанные алгоритмы, у меня был класс, принимающий параметр шаблона, чтобы выбрать, какой шаблон сдвига использовать. Я хотел знать, будет ли компилятор оптимизировать оператор switch, который я использовал. Я забыл передать семя, поэтому компилятор использовал значение по умолчанию, т.е. Семя было известно во время компиляции. Когда я посмотрел на код сборки, не только switch ушел, но GCC оптимизировал программу на "вывод этих трех чисел".

Окончательная версия программы, перечисленная в вопросе, никогда не называлась функциями для генерации последовательности чисел и никогда не вызывала функцию для семени генератора. Эта версия будет делать это, но я сомневаюсь, что она будет превращена в "печатать эту последовательность случайных чисел".

#include <algorithm>
#include <cstdlib>
#include <iostream>
#include <iterator>
#include <random>

int get_seed()
{
    int hour = std::atoi(__TIME__);
    int min = std::atoi(__TIME__ + 3);
    int sec = std::atoi(__TIME__ + 6);
    return 10000 * hour + 100 * min + sec;
}

int main()
{
    // get_seed() returns an int based on __TIME__ (a string literal
    // set by the preprocessor), which is known at compile time.
    //
    // Also, w/r/t the engines in <random>: not setting a seed explicitly
    // will use a default seed, which is known at compile time.  So if
    // you're OK getting the same sequence of numbers for any compilation,
    // then "std::mt19937_64 rng;" may be all you need.
    std::mt19937_64 rng(get_seed());
    std::uniform_real_distribution<double> zero_one(0.0, 1.0);
    const int COUNT = 1000;
    std::generate_n(std::ostream_iterator<double>(std::cout, "\n"), COUNT,
        [&rng, &zero_one]() { return zero_one(rng); });
    return 0;
}

Ответ 6

В соответствии с сообщением об ошибке:

cpp11tmprands.cpp:22:15: error: ‘rands’ was not declared in this scope

Переменная rands не объявлена ​​в области main. Сделайте глобальную переменную вместо локальной в generate_random и эта ошибка исчезнет.