#c #random #eigen
#c #Случайный #eigen
Вопрос:
Я пытаюсь инициализировать собственный вектор целых чисел случайными числами из определенного диапазона. Мой подход до сих пор заключался в создании функтора и его вызове, однако это проблематично в том смысле, что мне нужно инициализировать случайное поколение таким образом, чтобы оно не было повторно инициализировано каждый раз.
Какой был бы «лучший» способ сделать это? С точки зрения скорости.
Мой код для функтора на данный момент:
std::mt19937 rd(time(0));
std::default_random_engine gen(rd());
template<typename Scalar>
struct RandomRange {
RandomRange(const Scalaramp; low, const Scalaramp; high) : m_low(low), m_high(high) {}
const Scalar operator()(const Scalaramp; high) const {
std::uniform_int_distribution<> dis(m_low, m_high);
return dis(gen); }
Scalar m_low, m_high;
};
И я называю это использованием:
VectorXi testVec = VectorXi(10).unaryExpr(RandomRange<int>(5,100));
Побочный вопрос заключается в том, где было бы уместно поместить такое определение? Я довольно новичок в c в целом, но я полагаю, что это должно быть в файле заголовка, но я не уверен, как это будет работать с инициализацией генератора случайных чисел.
С наилучшими пожеланиями!
Комментарии:
1. Сделать
dis
(изменяемый или удалитьconst
изoperator()
) элементRandomRange
?2. @MaxLanghof
operator()
должен бытьconst
для собственного. Но сделатьdis
изменяемым — одно из возможных решений.
Ответ №1:
Во-первых, инициализация (заполнение) a default_random_engine
одним числом Мерсенна-твистера на самом деле не имеет смысла. Если простой движок случайной выборки достаточно хорош, непосредственно заполните его с помощью time(0)
или как вам больше нравится. Если вам нужны более длинные последовательности действительно независимых псевдослучайных чисел, передайте mt19937
объект непосредственно в ваш дистрибутив.
Кроме того, вы не используете свой high
аргумент operator()
, поэтому вам действительно следует использовать NullaryExpr
. Кроме того, вы можете создать dis
переменную-член и, вероятно, лучше сохранить ссылку на генератор вместо того, чтобы делать ее глобальной переменной:
template<typename Scalar>
struct RandomRange {
RandomRange(const Scalaramp; low, const Scalaramp; high,
std::default_random_engine amp;gen) : dis(low, high), gen(gen) {}
const Scalar operator()() const { return dis(gen); }
mutable std::uniform_int_distribution<> dis;
std::default_random_engine amp;gen;
};
И назовите это как:
std::default_random_engine gen(time(0));
Eigen::VectorXi testVec = Eigen::VectorXi::NullaryExpr(10,RandomRange<int>(5,100, gen));
или
std::default_random_engine gen(time(0));
RandomRange<int> uniform(5,100, gen)
Eigen::VectorXi testVec = Eigen::VectorXi::NullaryExpr(10, uniform);
С C 11 вы также можете просто определить свой дистрибутив локально и вызвать его, используя лямбда-выражение:
std::default_random_engine gen(time(0));
std::uniform_int_distribution<> dis(5,100);
Eigen::VectorXi testVec = Eigen::VectorXi::NullaryExpr(10,[amp;](){ return dis(gen); });
Или
std::default_random_engine gen(time(0));
std::uniform_int_distribution<> dis(5,100);
auto uni = [amp;](){ return dis(gen); };
Eigen::VectorXi testVec = Eigen::VectorXi::NullaryExpr(10,uni);
Или
std::default_random_engine gen(time(0));
Eigen::VectorXi testVec = Eigen::VectorXi::NullaryExpr(10,[amp;gen](){
std::uniform_int_distribution<> dis(5,100);
return dis(gen);
});
Некоторые примеры компиляции godbolt: https://godbolt.org/z/uG0j __
Если вы не обнаружите, что это бутылочное горлышко, я бы не стал слишком рано задумываться о том, какой вариант наиболее производителен (при надлежащей оптимизации все они должны быть эквивалентны), но используйте любой вариант, который наиболее прост для чтения и поддержки в вашей кодовой базе.