Эффективный способ инициализации собственной матрицы или вектора случайными числами из определенного диапазона?

#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 __

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