我如何创建一个充满随机数的向量?
通常的代码是:
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);} );
但是,这意味着每个值都被触摸两次:一旦设置为零,然后设置为随机值(even at O3)
那么如何尽可能高效地做到这一点呢?
你可以创建一个函数调用迭代器并将其传递给向量范围构造函数:
#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
方法相比,此方法不检查当前矢量大小与其容量,并且不增加每个元素的矢量大小。
从我发现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
编辑:也见于Is this correct way to combine std::generate_n and std::back_inserter?