代码之家  ›  专栏  ›  技术社区  ›  Flamefire

用随机数创建一个向量

  •  3
  • Flamefire  · 技术社区  · 6 年前

    一个充满随机数的向量?

    人们通常会发现这样的代码:

    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 )

    那么如何尽可能有效地做到这一点呢?

    2 回复  |  直到 6 年前
        1
  •  6
  •   Maxim Egorushkin    6 年前

    您可以进行函数调用迭代器并将其传递到向量范围构造函数:

    #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

        2
  •  4
  •   Flamefire    6 年前

    从我发现的 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?