Генерировать случайные числа в С++ во время компиляции
Я пытаюсь предварительно вычислить случайные значения, используя библиотеку С++ 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
Лучшим случайным генератором было бы написать программу, которая использует аудио и визуальные входы.
Ответ 4
Существует исследовательский документ по теме: Генератор случайных чисел для метапрограмм шаблонов С++
содержащий фрагмент кода для трюка __TIME__
. Он также говорит о поддержке различных двигателей и распределений случайных чисел в качестве ортогональных вариантов.
Ответ 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
и эта ошибка исчезнет.