//---------------------------------------------------------------------------// // Copyright (c) 2014 Roshan // // Distributed under the Boost Software License, Version 1.0 // See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt // // See http://boostorg.github.com/compute for more information. //---------------------------------------------------------------------------// #ifndef BOOST_COMPUTE_RANDOM_DISCRETE_DISTRIBUTION_HPP #define BOOST_COMPUTE_RANDOM_DISCRETE_DISTRIBUTION_HPP #include #include #include #include #include #include #include #include #include #include #include namespace boost { namespace compute { /// \class discrete_distribution /// \brief Produces random integers on the interval [0, n), where /// probability of each integer is given by the weight of the ith /// integer divided by the sum of all weights. /// /// The following example shows how to setup a discrete distribution to /// produce 0 and 1 with equal probability /// /// \snippet test/test_discrete_distribution.cpp generate /// template class discrete_distribution { public: typedef IntType result_type; /// Creates a new discrete distribution with a single weight p = { 1 }. /// This distribution produces only zeroes. discrete_distribution() : m_probabilities(1, double(1)), m_scanned_probabilities(1, double(1)) { } /// Creates a new discrete distribution with weights given by /// the range [\p first, \p last). template discrete_distribution(InputIterator first, InputIterator last) : m_probabilities(first, last), m_scanned_probabilities(std::distance(first, last)) { if(first != last) { // after this m_scanned_probabilities.back() is a sum of all // weights from the range [first, last) std::partial_sum(first, last, m_scanned_probabilities.begin()); std::vector::iterator i = m_probabilities.begin(); std::vector::iterator j = m_scanned_probabilities.begin(); for(; i != m_probabilities.end(); ++i, ++j) { // dividing each weight by sum of all weights to // get probabilities *i = *i / m_scanned_probabilities.back(); // dividing each partial sum of weights by sum of // all weights to get partial sums of probabilities *j = *j / m_scanned_probabilities.back(); } } else { m_probabilities.push_back(double(1)); m_scanned_probabilities.push_back(double(1)); } } /// Destroys the discrete_distribution object. ~discrete_distribution() { } /// Returns the probabilities ::std::vector probabilities() const { return m_probabilities; } /// Returns the minimum potentially generated value. result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const { return result_type(0); } /// Returns the maximum potentially generated value. result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const { size_t type_max = static_cast( (std::numeric_limits::max)() ); if(m_probabilities.size() - 1 > type_max) { return (std::numeric_limits::max)(); } return static_cast(m_probabilities.size() - 1); } /// Generates uniformly distributed integers and stores /// them to the range [\p first, \p last). template void generate(OutputIterator first, OutputIterator last, Generator &generator, command_queue &queue) { std::string source = "inline IntType scale_random(uint x)\n"; source = source + "{\n" + "float rno = convert_float(x) / UINT_MAX;\n"; for(size_t i = 0; i < m_scanned_probabilities.size() - 1; i++) { source = source + "if(rno <= " + detail::make_literal(m_scanned_probabilities[i]) + ")\n" + " return " + detail::make_literal(i) + ";\n"; } source = source + "return " + detail::make_literal(m_scanned_probabilities.size() - 1) + ";\n" + "}\n"; BOOST_COMPUTE_FUNCTION(IntType, scale_random, (const uint_ x), {}); scale_random.set_source(source); scale_random.define("IntType", type_name()); generator.generate(first, last, scale_random, queue); } private: ::std::vector m_probabilities; ::std::vector m_scanned_probabilities; BOOST_STATIC_ASSERT_MSG( boost::is_integral::value, "Template argument must be integral" ); }; } // end compute namespace } // end boost namespace #endif // BOOST_COMPUTE_RANDOM_UNIFORM_INT_DISTRIBUTION_HPP