Эффективный способ инициализировать собственную матрицу или вектор случайными числами из определенного диапазона? - PullRequest
1 голос
/ 20 марта 2019

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

Каков был бы «лучший» способ сделать это? Speed-накрест.

Мой код для функтора на данный момент:

std::mt19937 rd(time(0));
std::default_random_engine gen(rd());

template<typename Scalar>
struct RandomRange {
    RandomRange(const Scalar& low, const Scalar& high) : m_low(low), m_high(high) {}
    const Scalar operator()(const Scalar& 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 Ответ

2 голосов
/ 21 марта 2019

Прежде всего, инициализация (засев) default_random_engine одним номером закрученного Мерсенна на самом деле не имеет смысла.Если простой случайный движок достаточно хорош, сразу заполняйте его time(0) или любым другим параметром.Если вам нужны более длинные последовательности действительно независимых псевдослучайных чисел, напрямую передайте объект mt19937 в свой дистрибутив.

Кроме того, вы не используете аргумент high вашего operator(), поэтому вам следуетна самом деле использовать NullaryExpr.Кроме того, вы можете сделать dis переменной-членом и, возможно, лучше хранить ссылку на генератор, а не делать ее глобальной переменной:

template<typename Scalar>
struct RandomRange {
    RandomRange(const Scalar& low, const Scalar& high, 
                std::default_random_engine &gen) : dis(low, high), gen(gen) {}
    const Scalar operator()() const { return dis(gen); }
    mutable std::uniform_int_distribution<> dis;
    std::default_random_engine &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,[&](){ return dis(gen); });

Или

std::default_random_engine gen(time(0));
std::uniform_int_distribution<> dis(5,100);
auto uni = [&](){ 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,[&gen](){
    std::uniform_int_distribution<> dis(5,100);
    return dis(gen);
});

Некоторые примеры компиляции Годболта: https://godbolt.org/z/uG0j__

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

...