Создать вектор со случайными числами - PullRequest
0 голосов
/ 14 сентября 2018

Как бы я создал вектор, заполненный случайными числами?

Обычный код, который можно найти, выглядит следующим образом:

std::mt19937 rng {std::random_device{}()};
std::uniform_int_distribution<int> dist {1, 52};

std::vector<int> vec(10);
std::generate(begin(vec), end(vec), [&]{return dist(rng);} );

Однако это означает, что к каждому значению прикасаются дважды: после установки на ноль, а затем на случайное значение ( даже при O3 )

Так как же сделать это максимально эффективно?

Ответы [ 2 ]

0 голосов
/ 14 сентября 2018

Вы можете создать итератор вызова функции и передать его в конструктор векторного диапазона:

#include <boost/iterator/iterator_facade.hpp>
#include <iostream>
#include <vector>
#include <random>
#include <tuple>

template<class F, class Tag = std::input_iterator_tag>
class FunctionCallIterator
    : public boost::iterator_facade<
          FunctionCallIterator<F, Tag>,
          typename std::result_of<F()>::type,
          Tag,
          typename std::result_of<F()>::type
      >
{
    std::tuple<F, ptrdiff_t> m_; // Enable empty base class optimization for empty F.
    friend class boost::iterator_core_access;
    typename std::result_of<F()>::type dereference() const { return std::get<0>(m_)(); }
    bool equal(FunctionCallIterator const& b) const { return std::get<1>(m_) == std::get<1>(b.m_); }
    void increment() { ++std::get<1>(m_); }
    void decrement() { --std::get<1>(m_); }
    void advance(ptrdiff_t n) { std::get<1>(m_) += n; }
    ptrdiff_t distance_to(FunctionCallIterator const& b) const { return std::get<1>(b.m_) - std::get<1>(m_); }
public:
    FunctionCallIterator(F const& f, ptrdiff_t n) : m_(f, n) {}
};

int main() {
    std::mt19937 rng {std::random_device{}()};
    std::uniform_int_distribution<int> dist {1, 52};
    auto f = [&]{return dist(rng);};
    using RngIter = FunctionCallIterator<decltype(f), std::random_access_iterator_tag>;
    std::vector<int> vec(RngIter{f, 0}, RngIter{f, 10});
    for(auto v : vec)
        std::cout << v << '\n';
}

По сравнению с методом push_back / back_inserter этот метод не проверяет текущий размер вектора в сравнении с его емкостью и не увеличивает размер вектора для каждого элемента.

0 голосов
/ 14 сентября 2018

Из того, что я нашел, комбинация reserve с back_inserter должна помочь:

std::mt19937 rng {std::random_device{}()};
std::uniform_int_distribution<int> dist {1, 52};

std::vector<int> vec;
const size_t size = 1000;
vec.reserve(size);
std::generate_n(std::back_inserter(vec), size, [&]{return dist(rng);} );

Это кажется очень эффективным, но все еще есть проверка емкости, которая не должна бытьобязательно: https://godbolt.org/z/sOBlLx

Не уверен, если std::vector позволяет что-либо быть более эффективным, чем это.Требуется uninitialized_resize

Редактировать: Также можно увидеть на Это правильный способ объединения std :: generate_n и std :: back_inserter?

...