467 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			467 lines
		
	
	
		
			17 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|   | /* boost random/piecewise_constant_distribution.hpp header file | ||
|  |  * | ||
|  |  * Copyright Steven Watanabe 2011 | ||
|  |  * 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://www.boost.org for most recent version including documentation. | ||
|  |  * | ||
|  |  * $Id$ | ||
|  |  */ | ||
|  | 
 | ||
|  | #ifndef BOOST_RANDOM_PIECEWISE_CONSTANT_DISTRIBUTION_HPP_INCLUDED | ||
|  | #define BOOST_RANDOM_PIECEWISE_CONSTANT_DISTRIBUTION_HPP_INCLUDED | ||
|  | 
 | ||
|  | #include <vector> | ||
|  | #include <numeric> | ||
|  | #include <boost/assert.hpp> | ||
|  | #include <boost/random/uniform_real.hpp> | ||
|  | #include <boost/random/discrete_distribution.hpp> | ||
|  | #include <boost/random/detail/config.hpp> | ||
|  | #include <boost/random/detail/operators.hpp> | ||
|  | #include <boost/random/detail/vector_io.hpp> | ||
|  | 
 | ||
|  | #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST | ||
|  | #include <initializer_list> | ||
|  | #endif | ||
|  | 
 | ||
|  | #include <boost/range/begin.hpp> | ||
|  | #include <boost/range/end.hpp> | ||
|  | 
 | ||
|  | namespace boost { | ||
|  | namespace random { | ||
|  | 
 | ||
|  | /** | ||
|  |  * The class @c piecewise_constant_distribution models a \random_distribution. | ||
|  |  */ | ||
|  | template<class RealType = double, class WeightType = double> | ||
|  | class piecewise_constant_distribution { | ||
|  | public: | ||
|  |     typedef std::size_t input_type; | ||
|  |     typedef RealType result_type; | ||
|  | 
 | ||
|  |     class param_type { | ||
|  |     public: | ||
|  | 
 | ||
|  |         typedef piecewise_constant_distribution distribution_type; | ||
|  | 
 | ||
|  |         /** | ||
|  |          * Constructs a @c param_type object, representing a distribution | ||
|  |          * that produces values uniformly distributed in the range [0, 1). | ||
|  |          */ | ||
|  |         param_type() | ||
|  |         { | ||
|  |             _weights.push_back(WeightType(1)); | ||
|  |             _intervals.push_back(RealType(0)); | ||
|  |             _intervals.push_back(RealType(1)); | ||
|  |         } | ||
|  |         /** | ||
|  |          * Constructs a @c param_type object from two iterator ranges | ||
|  |          * containing the interval boundaries and the interval weights. | ||
|  |          * If there are less than two boundaries, then this is equivalent to | ||
|  |          * the default constructor and creates a single interval, [0, 1). | ||
|  |          * | ||
|  |          * The values of the interval boundaries must be strictly | ||
|  |          * increasing, and the number of weights must be one less than | ||
|  |          * the number of interval boundaries.  If there are extra | ||
|  |          * weights, they are ignored. | ||
|  |          */ | ||
|  |         template<class IntervalIter, class WeightIter> | ||
|  |         param_type(IntervalIter intervals_first, IntervalIter intervals_last, | ||
|  |                    WeightIter weight_first) | ||
|  |           : _intervals(intervals_first, intervals_last) | ||
|  |         { | ||
|  |             if(_intervals.size() < 2) { | ||
|  |                 _intervals.clear(); | ||
|  |                 _intervals.push_back(RealType(0)); | ||
|  |                 _intervals.push_back(RealType(1)); | ||
|  |                 _weights.push_back(WeightType(1)); | ||
|  |             } else { | ||
|  |                 _weights.reserve(_intervals.size() - 1); | ||
|  |                 for(std::size_t i = 0; i < _intervals.size() - 1; ++i) { | ||
|  |                     _weights.push_back(*weight_first++); | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  | #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST | ||
|  |         /** | ||
|  |          * Constructs a @c param_type object from an | ||
|  |          * initializer_list containing the interval boundaries | ||
|  |          * and a unary function specifying the weights.  Each | ||
|  |          * weight is determined by calling the function at the | ||
|  |          * midpoint of the corresponding interval. | ||
|  |          * | ||
|  |          * If the initializer_list contains less than two elements, | ||
|  |          * this is equivalent to the default constructor and the | ||
|  |          * distribution will produce values uniformly distributed | ||
|  |          * in the range [0, 1). | ||
|  |          */ | ||
|  |         template<class T, class F> | ||
|  |         param_type(const std::initializer_list<T>& il, F f) | ||
|  |           : _intervals(il.begin(), il.end()) | ||
|  |         { | ||
|  |             if(_intervals.size() < 2) { | ||
|  |                 _intervals.clear(); | ||
|  |                 _intervals.push_back(RealType(0)); | ||
|  |                 _intervals.push_back(RealType(1)); | ||
|  |                 _weights.push_back(WeightType(1)); | ||
|  |             } else { | ||
|  |                 _weights.reserve(_intervals.size() - 1); | ||
|  |                 for(std::size_t i = 0; i < _intervals.size() - 1; ++i) { | ||
|  |                     RealType midpoint = (_intervals[i] + _intervals[i + 1]) / 2; | ||
|  |                     _weights.push_back(f(midpoint)); | ||
|  |                 } | ||
|  |             } | ||
|  |         } | ||
|  | #endif | ||
|  |         /** | ||
|  |          * Constructs a @c param_type object from Boost.Range | ||
|  |          * ranges holding the interval boundaries and the weights.  If | ||
|  |          * there are less than two interval boundaries, this is equivalent | ||
|  |          * to the default constructor and the distribution will produce | ||
|  |          * values uniformly distributed in the range [0, 1).  The | ||
|  |          * number of weights must be one less than the number of | ||
|  |          * interval boundaries. | ||
|  |          */ | ||
|  |         template<class IntervalRange, class WeightRange> | ||
|  |         param_type(const IntervalRange& intervals_arg, | ||
|  |                    const WeightRange& weights_arg) | ||
|  |           : _intervals(boost::begin(intervals_arg), boost::end(intervals_arg)), | ||
|  |             _weights(boost::begin(weights_arg), boost::end(weights_arg)) | ||
|  |         { | ||
|  |             if(_intervals.size() < 2) { | ||
|  |                 _intervals.clear(); | ||
|  |                 _intervals.push_back(RealType(0)); | ||
|  |                 _intervals.push_back(RealType(1)); | ||
|  |                 _weights.push_back(WeightType(1)); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         /** | ||
|  |          * Constructs the parameters for a distribution that approximates a | ||
|  |          * function.  The range of the distribution is [xmin, xmax).  This | ||
|  |          * range is divided into nw equally sized intervals and the weights | ||
|  |          * are found by calling the unary function f on the midpoints of the | ||
|  |          * intervals. | ||
|  |          */ | ||
|  |         template<class F> | ||
|  |         param_type(std::size_t nw, RealType xmin, RealType xmax, F f) | ||
|  |         { | ||
|  |             std::size_t n = (nw == 0) ? 1 : nw; | ||
|  |             double delta = (xmax - xmin) / n; | ||
|  |             BOOST_ASSERT(delta > 0); | ||
|  |             for(std::size_t k = 0; k < n; ++k) { | ||
|  |                 _weights.push_back(f(xmin + k*delta + delta/2)); | ||
|  |                 _intervals.push_back(xmin + k*delta); | ||
|  |             } | ||
|  |             _intervals.push_back(xmax); | ||
|  |         } | ||
|  | 
 | ||
|  |         /**  Returns a vector containing the interval boundaries. */ | ||
|  |         std::vector<RealType> intervals() const { return _intervals; } | ||
|  | 
 | ||
|  |         /** | ||
|  |          * Returns a vector containing the probability densities | ||
|  |          * over all the intervals of the distribution. | ||
|  |          */ | ||
|  |         std::vector<RealType> densities() const | ||
|  |         { | ||
|  |             RealType sum = std::accumulate(_weights.begin(), _weights.end(), | ||
|  |                                              static_cast<RealType>(0)); | ||
|  |             std::vector<RealType> result; | ||
|  |             result.reserve(_weights.size()); | ||
|  |             for(std::size_t i = 0; i < _weights.size(); ++i) { | ||
|  |                 RealType width = _intervals[i + 1] - _intervals[i]; | ||
|  |                 result.push_back(_weights[i] / (sum * width)); | ||
|  |             } | ||
|  |             return result; | ||
|  |         } | ||
|  | 
 | ||
|  |         /** Writes the parameters to a @c std::ostream. */ | ||
|  |         BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR(os, param_type, parm) | ||
|  |         { | ||
|  |             detail::print_vector(os, parm._intervals); | ||
|  |             detail::print_vector(os, parm._weights); | ||
|  |             return os; | ||
|  |         } | ||
|  |          | ||
|  |         /** Reads the parameters from a @c std::istream. */ | ||
|  |         BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR(is, param_type, parm) | ||
|  |         { | ||
|  |             std::vector<RealType> new_intervals; | ||
|  |             std::vector<WeightType> new_weights; | ||
|  |             detail::read_vector(is, new_intervals); | ||
|  |             detail::read_vector(is, new_weights); | ||
|  |             if(is) { | ||
|  |                 parm._intervals.swap(new_intervals); | ||
|  |                 parm._weights.swap(new_weights); | ||
|  |             } | ||
|  |             return is; | ||
|  |         } | ||
|  | 
 | ||
|  |         /** Returns true if the two sets of parameters are the same. */ | ||
|  |         BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR(param_type, lhs, rhs) | ||
|  |         { | ||
|  |             return lhs._intervals == rhs._intervals | ||
|  |                 && lhs._weights == rhs._weights; | ||
|  |         } | ||
|  |         /** Returns true if the two sets of parameters are different. */ | ||
|  |         BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(param_type) | ||
|  | 
 | ||
|  |     private: | ||
|  | 
 | ||
|  |         friend class piecewise_constant_distribution; | ||
|  | 
 | ||
|  |         std::vector<RealType> _intervals; | ||
|  |         std::vector<WeightType> _weights; | ||
|  |     }; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Creates a new @c piecewise_constant_distribution with | ||
|  |      * a single interval, [0, 1). | ||
|  |      */ | ||
|  |     piecewise_constant_distribution() | ||
|  |     { | ||
|  |         _intervals.push_back(RealType(0)); | ||
|  |         _intervals.push_back(RealType(1)); | ||
|  |     } | ||
|  |     /** | ||
|  |      * Constructs a piecewise_constant_distribution from two iterator ranges | ||
|  |      * containing the interval boundaries and the interval weights. | ||
|  |      * If there are less than two boundaries, then this is equivalent to | ||
|  |      * the default constructor and creates a single interval, [0, 1). | ||
|  |      * | ||
|  |      * The values of the interval boundaries must be strictly | ||
|  |      * increasing, and the number of weights must be one less than | ||
|  |      * the number of interval boundaries.  If there are extra | ||
|  |      * weights, they are ignored. | ||
|  |      * | ||
|  |      * For example, | ||
|  |      * | ||
|  |      * @code | ||
|  |      * double intervals[] = { 0.0, 1.0, 4.0 }; | ||
|  |      * double weights[] = { 1.0, 1.0 }; | ||
|  |      * piecewise_constant_distribution<> dist( | ||
|  |      *     &intervals[0], &intervals[0] + 3, &weights[0]); | ||
|  |      * @endcode | ||
|  |      * | ||
|  |      * The distribution has a 50% chance of producing a | ||
|  |      * value between 0 and 1 and a 50% chance of producing | ||
|  |      * a value between 1 and 4. | ||
|  |      */ | ||
|  |     template<class IntervalIter, class WeightIter> | ||
|  |     piecewise_constant_distribution(IntervalIter first_interval, | ||
|  |                                     IntervalIter last_interval, | ||
|  |                                     WeightIter first_weight) | ||
|  |       : _intervals(first_interval, last_interval) | ||
|  |     { | ||
|  |         if(_intervals.size() < 2) { | ||
|  |             _intervals.clear(); | ||
|  |             _intervals.push_back(RealType(0)); | ||
|  |             _intervals.push_back(RealType(1)); | ||
|  |         } else { | ||
|  |             std::vector<WeightType> actual_weights; | ||
|  |             actual_weights.reserve(_intervals.size() - 1); | ||
|  |             for(std::size_t i = 0; i < _intervals.size() - 1; ++i) { | ||
|  |                 actual_weights.push_back(*first_weight++); | ||
|  |             } | ||
|  |             typedef discrete_distribution<std::size_t, WeightType> bins_type; | ||
|  |             typename bins_type::param_type bins_param(actual_weights); | ||
|  |             _bins.param(bins_param); | ||
|  |         } | ||
|  |     } | ||
|  | #ifndef BOOST_NO_CXX11_HDR_INITIALIZER_LIST | ||
|  |     /** | ||
|  |      * Constructs a piecewise_constant_distribution from an | ||
|  |      * initializer_list containing the interval boundaries | ||
|  |      * and a unary function specifying the weights.  Each | ||
|  |      * weight is determined by calling the function at the | ||
|  |      * midpoint of the corresponding interval. | ||
|  |      * | ||
|  |      * If the initializer_list contains less than two elements, | ||
|  |      * this is equivalent to the default constructor and the | ||
|  |      * distribution will produce values uniformly distributed | ||
|  |      * in the range [0, 1). | ||
|  |      */ | ||
|  |     template<class T, class F> | ||
|  |     piecewise_constant_distribution(std::initializer_list<T> il, F f) | ||
|  |       : _intervals(il.begin(), il.end()) | ||
|  |     { | ||
|  |         if(_intervals.size() < 2) { | ||
|  |             _intervals.clear(); | ||
|  |             _intervals.push_back(RealType(0)); | ||
|  |             _intervals.push_back(RealType(1)); | ||
|  |         } else { | ||
|  |             std::vector<WeightType> actual_weights; | ||
|  |             actual_weights.reserve(_intervals.size() - 1); | ||
|  |             for(std::size_t i = 0; i < _intervals.size() - 1; ++i) { | ||
|  |                 RealType midpoint = (_intervals[i] + _intervals[i + 1]) / 2; | ||
|  |                 actual_weights.push_back(f(midpoint)); | ||
|  |             } | ||
|  |             typedef discrete_distribution<std::size_t, WeightType> bins_type; | ||
|  |             typename bins_type::param_type bins_param(actual_weights); | ||
|  |             _bins.param(bins_param); | ||
|  |         } | ||
|  |     } | ||
|  | #endif | ||
|  |     /** | ||
|  |      * Constructs a piecewise_constant_distribution from Boost.Range | ||
|  |      * ranges holding the interval boundaries and the weights.  If | ||
|  |      * there are less than two interval boundaries, this is equivalent | ||
|  |      * to the default constructor and the distribution will produce | ||
|  |      * values uniformly distributed in the range [0, 1).  The | ||
|  |      * number of weights must be one less than the number of | ||
|  |      * interval boundaries. | ||
|  |      */ | ||
|  |     template<class IntervalsRange, class WeightsRange> | ||
|  |     piecewise_constant_distribution(const IntervalsRange& intervals_arg, | ||
|  |                                     const WeightsRange& weights_arg) | ||
|  |       : _bins(weights_arg), | ||
|  |         _intervals(boost::begin(intervals_arg), boost::end(intervals_arg)) | ||
|  |     { | ||
|  |         if(_intervals.size() < 2) { | ||
|  |             _intervals.clear(); | ||
|  |             _intervals.push_back(RealType(0)); | ||
|  |             _intervals.push_back(RealType(1)); | ||
|  |         } | ||
|  |     } | ||
|  |     /** | ||
|  |      * Constructs a piecewise_constant_distribution that approximates a | ||
|  |      * function.  The range of the distribution is [xmin, xmax).  This | ||
|  |      * range is divided into nw equally sized intervals and the weights | ||
|  |      * are found by calling the unary function f on the midpoints of the | ||
|  |      * intervals. | ||
|  |      */ | ||
|  |     template<class F> | ||
|  |     piecewise_constant_distribution(std::size_t nw, | ||
|  |                                     RealType xmin, | ||
|  |                                     RealType xmax, | ||
|  |                                     F f) | ||
|  |       : _bins(nw, xmin, xmax, f) | ||
|  |     { | ||
|  |         if(nw == 0) { nw = 1; } | ||
|  |         RealType delta = (xmax - xmin) / nw; | ||
|  |         _intervals.reserve(nw + 1); | ||
|  |         for(std::size_t i = 0; i < nw; ++i) { | ||
|  |             _intervals.push_back(xmin + i * delta); | ||
|  |         } | ||
|  |         _intervals.push_back(xmax); | ||
|  |     } | ||
|  |     /** | ||
|  |      * Constructs a piecewise_constant_distribution from its parameters. | ||
|  |      */ | ||
|  |     explicit piecewise_constant_distribution(const param_type& parm) | ||
|  |       : _bins(parm._weights), | ||
|  |         _intervals(parm._intervals) | ||
|  |     { | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns a value distributed according to the parameters of the | ||
|  |      * piecewist_constant_distribution. | ||
|  |      */ | ||
|  |     template<class URNG> | ||
|  |     RealType operator()(URNG& urng) const | ||
|  |     { | ||
|  |         std::size_t i = _bins(urng); | ||
|  |         return uniform_real<RealType>(_intervals[i], _intervals[i+1])(urng); | ||
|  |     } | ||
|  |      | ||
|  |     /** | ||
|  |      * Returns a value distributed according to the parameters | ||
|  |      * specified by param. | ||
|  |      */ | ||
|  |     template<class URNG> | ||
|  |     RealType operator()(URNG& urng, const param_type& parm) const | ||
|  |     { | ||
|  |         return piecewise_constant_distribution(parm)(urng); | ||
|  |     } | ||
|  |      | ||
|  |     /** Returns the smallest value that the distribution can produce. */ | ||
|  |     result_type min BOOST_PREVENT_MACRO_SUBSTITUTION () const | ||
|  |     { return _intervals.front(); } | ||
|  |     /** Returns the largest value that the distribution can produce. */ | ||
|  |     result_type max BOOST_PREVENT_MACRO_SUBSTITUTION () const | ||
|  |     { return _intervals.back(); } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns a vector containing the probability density | ||
|  |      * over each interval. | ||
|  |      */ | ||
|  |     std::vector<RealType> densities() const | ||
|  |     { | ||
|  |         std::vector<RealType> result(_bins.probabilities()); | ||
|  |         for(std::size_t i = 0; i < result.size(); ++i) { | ||
|  |             result[i] /= (_intervals[i+1] - _intervals[i]); | ||
|  |         } | ||
|  |         return(result); | ||
|  |     } | ||
|  |     /**  Returns a vector containing the interval boundaries. */ | ||
|  |     std::vector<RealType> intervals() const { return _intervals; } | ||
|  | 
 | ||
|  |     /** Returns the parameters of the distribution. */ | ||
|  |     param_type param() const | ||
|  |     { | ||
|  |         return param_type(_intervals, _bins.probabilities()); | ||
|  |     } | ||
|  |     /** Sets the parameters of the distribution. */ | ||
|  |     void param(const param_type& parm) | ||
|  |     { | ||
|  |         std::vector<RealType> new_intervals(parm._intervals); | ||
|  |         typedef discrete_distribution<std::size_t, WeightType> bins_type; | ||
|  |         typename bins_type::param_type bins_param(parm._weights); | ||
|  |         _bins.param(bins_param); | ||
|  |         _intervals.swap(new_intervals); | ||
|  |     } | ||
|  |      | ||
|  |     /** | ||
|  |      * Effects: Subsequent uses of the distribution do not depend | ||
|  |      * on values produced by any engine prior to invoking reset. | ||
|  |      */ | ||
|  |     void reset() { _bins.reset(); } | ||
|  | 
 | ||
|  |     /** Writes a distribution to a @c std::ostream. */ | ||
|  |     BOOST_RANDOM_DETAIL_OSTREAM_OPERATOR( | ||
|  |         os, piecewise_constant_distribution, pcd) | ||
|  |     { | ||
|  |         os << pcd.param(); | ||
|  |         return os; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** Reads a distribution from a @c std::istream */ | ||
|  |     BOOST_RANDOM_DETAIL_ISTREAM_OPERATOR( | ||
|  |         is, piecewise_constant_distribution, pcd) | ||
|  |     { | ||
|  |         param_type parm; | ||
|  |         if(is >> parm) { | ||
|  |             pcd.param(parm); | ||
|  |         } | ||
|  |         return is; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Returns true if the two distributions will return the | ||
|  |      * same sequence of values, when passed equal generators. | ||
|  |      */ | ||
|  |     BOOST_RANDOM_DETAIL_EQUALITY_OPERATOR( | ||
|  |         piecewise_constant_distribution, lhs,  rhs) | ||
|  |     { | ||
|  |         return lhs._bins == rhs._bins && lhs._intervals == rhs._intervals; | ||
|  |     } | ||
|  |     /** | ||
|  |      * Returns true if the two distributions may return different | ||
|  |      * sequences of values, when passed equal generators. | ||
|  |      */ | ||
|  |     BOOST_RANDOM_DETAIL_INEQUALITY_OPERATOR(piecewise_constant_distribution) | ||
|  | 
 | ||
|  | private: | ||
|  |     discrete_distribution<std::size_t, WeightType> _bins; | ||
|  |     std::vector<RealType> _intervals; | ||
|  | }; | ||
|  | 
 | ||
|  | } | ||
|  | } | ||
|  | 
 | ||
|  | #endif |