mirror of
https://github.com/XRPLF/rippled.git
synced 2025-12-06 17:27:55 +00:00
181 lines
5.2 KiB
C++
181 lines
5.2 KiB
C++
//------------------------------------------------------------------------------
|
|
/*
|
|
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 <random>
|
|
#include <vector>
|
|
|
|
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 <class T, class G>
|
|
std::vector<T>
|
|
random_weighted_shuffle(std::vector<T> v, std::vector<double> 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 <class RandomNumberDistribution, class Generator>
|
|
std::vector<typename RandomNumberDistribution::result_type>
|
|
sample(std::size_t size, RandomNumberDistribution dist, Generator& g)
|
|
{
|
|
std::vector<typename RandomNumberDistribution::result_type> 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 RAIter, class Generator>
|
|
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<double> const& w,
|
|
Generator& g)
|
|
: first_{first}, last_{last}, dd_{w.begin(), w.end()}, g_{g}
|
|
{
|
|
using tag = typename std::iterator_traits<RAIter>::iterator_category;
|
|
static_assert(
|
|
std::is_same<tag, std::random_access_iterator_tag>::value,
|
|
"Selector only supports random access iterators.");
|
|
// TODO: Allow for forward iterators
|
|
}
|
|
|
|
typename std::iterator_traits<RAIter>::value_type
|
|
operator()()
|
|
{
|
|
auto idx = dd_(g_);
|
|
return *(first_ + idx);
|
|
}
|
|
};
|
|
|
|
template <typename Iter, typename Generator>
|
|
Selector<Iter, Generator>
|
|
makeSelector(Iter first, Iter last, std::vector<double> const& w, Generator& g)
|
|
{
|
|
return Selector<Iter, Generator>(first, last, w, g);
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Additional distributions of interest not defined in <random>
|
|
|
|
/** Constant "distribution" that always returns the same value
|
|
*/
|
|
class ConstantDistribution
|
|
{
|
|
double t_;
|
|
|
|
public:
|
|
ConstantDistribution(double const& t) : t_{t}
|
|
{
|
|
}
|
|
|
|
template <class Generator>
|
|
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<double> uf_{0, 1};
|
|
|
|
public:
|
|
using result_type = double;
|
|
|
|
PowerLawDistribution(double xmin, double a) : xmin_{xmin}, a_{a}
|
|
{
|
|
inv_ = 1.0 / (1.0 - a_);
|
|
}
|
|
|
|
template <class Generator>
|
|
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
|