//------------------------------------------------------------------------------ /* This file is part of rippled: https://github.com/ripple/rippled Copyright (c) 2012-2017 Ripple Labs Inc Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ //============================================================================== #ifndef RIPPLE_TEST_CSF_RANDOM_H_INCLUDED #define RIPPLE_TEST_CSF_RANDOM_H_INCLUDED #include #include namespace ripple { namespace test { namespace csf { /** Return a randomly shuffled copy of vector based on weights w. @param v The set of values @param w The set of weights of each value @param g A pseudo-random number generator @return A vector with entries randomly sampled without replacement from the original vector based on the provided weights. I.e. res[0] comes from sample v[i] with weight w[i]/sum_k w[k] */ template std::vector random_weighted_shuffle(std::vector v, std::vector w, G& g) { using std::swap; for (int i = 0; i < v.size() - 1; ++i) { // pick a random item weighted by w std::discrete_distribution<> dd(w.begin() + i, w.end()); auto idx = dd(g); std::swap(v[i], v[idx]); std::swap(w[i], w[idx]); } return v; } /** Generate a vector of random samples @param size the size of the sample @param dist the distribution to sample @param g the pseudo-random number generator @return vector of samples */ template std::vector sample(std::size_t size, RandomNumberDistribution dist, Generator& g) { std::vector res(size); std::generate(res.begin(), res.end(), [&dist, &g]() { return dist(g); }); return res; } /** Invocable that returns random samples from a range according to a discrete distribution Given a pair of random access iterators begin and end, each call to the instance of Selector returns a random entry in the range (begin,end) according to the weights provided at construction. */ template class Selector { RAIter first_, last_; std::discrete_distribution<> dd_; Generator g_; public: /** Constructor @param first Random access iterator to the start of the range @param last Random access iterator to the end of the range @param w Vector of weights of size list-first @param g the pseudo-random number generator */ Selector( RAIter first, RAIter last, std::vector const& w, Generator& g) : first_{first}, last_{last}, dd_{w.begin(), w.end()}, g_{g} { using tag = typename std::iterator_traits::iterator_category; static_assert( std::is_same::value, "Selector only supports random access iterators."); // TODO: Allow for forward iterators } typename std::iterator_traits::value_type operator()() { auto idx = dd_(g_); return *(first_ + idx); } }; template Selector makeSelector(Iter first, Iter last, std::vector const& w, Generator& g) { return Selector(first, last, w, g); } //------------------------------------------------------------------------------ // Additional distributions of interest not defined in /** Constant "distribution" that always returns the same value */ class ConstantDistribution { double t_; public: ConstantDistribution(double const& t) : t_{t} { } template inline double operator()(Generator&) { return t_; } }; /** Power-law distribution with PDF P(x) = (x/xmin)^-a for a >= 1 and xmin >= 1 */ class PowerLawDistribution { double xmin_; double a_; double inv_; std::uniform_real_distribution uf_{0, 1}; public: using result_type = double; PowerLawDistribution(double xmin, double a) : xmin_{xmin}, a_{a} { inv_ = 1.0 / (1.0 - a_); } template inline double operator()(Generator& g) { // use inverse transform of CDF to sample // CDF is P(X <= x): 1 - (x/xmin)^(1-a) return xmin_ * std::pow(1 - uf_(g), inv_); } }; } // namespace csf } // namespace test } // namespace ripple #endif