xso::generator
— Sampling Methods
Single Samples
We provide methods to extract a single sample from an interval, arbitrary distribution, and a container. Another method also picks a random index given an array size.
Uniform Distributions
template<std::integral T>
1constexpr T sample(T a, T b)
template<std::integral T>
2constexpr T index(T n);
template<std::floating_point T>
3constexpr T sample(T a, T b);
template<std::input_iterator T>
4constexpr auto sample(T b, T e);
template<typename Container>
5constexpr auto sample(const Container &c);
- 1
- Returns a single integer value from a uniform distribution over \([a,b]\). No error checks are performed, and the behaviour is undefined if \(a < b\).
- 2
- Returns a single index from a uniform distribution over \([0,n)\). No error checks are performed, and the behaviour is undefined if \(n=0\).
- 3
- Returns a single real value from a uniform distribution over \([a,b)\). No error checks are performed, and the behaviour is undefined if \(a < b\).
- 4
- Returns a single element from the iteration \([b,e)\), where all elements are equally likely to be returned. No error checks are performed, and the behaviour is undefined if \(e < b\).
- 5
-
Returns a single element from container
c
, where all elements are equally likely to be returned. The method will work on any containerc
which supportsstd::cbegin(c)
andstd::cend(c)
.
Arbitrary Distributions
We have a C++ [concept][`] that captures a common feature of the distributions defined in the standard {std.random} header: ```cpp template<typename D> concept xso::Distribution = requires { typename D::param_type; }; // <1> ``` 1. All those distributions define an embedded
param_type` — this is distinct from the standard collections like std::vector
.
We can use that concept to overload the sample
methods with xso::Distribution
arguments as follows:
1constexpr auto sample(Distribution auto &dist);
- 1
-
Returns a single variate from the distribution
dist
e.g. from astd::normal_distribution
instance.
NOTE It is convenient to have many sampling methods, all named sample(…), and C++ allows this through overloading. The specific sample
version invoked depends on the type of arguments passed. To make this work, we must distinguish between a container like a std::vector
and a distribution like a std::normal_distribution
. The xso::Distribution
concept is a minimal way to identify the distributions defined in the standard library. It relies on all distribution classes having an embedded typename
defining a param_type
. None of the standard container classes have that.
Examples
Extract single samples from intervals, distributions, and containers:
#include <utilities/utilities.h>
#include <xoshiro.h>
int main()
{
::rng gen;
xsostd::size_t n, n_samples = 3;
std::cout << "Characters from ['a','z']\n";
for (n = 0; n < n_samples; ++n) std::print("'{:c}' ", gen.sample(std::uint8_t{'a'}, std::uint8_t{'z'}));
std::print("\n");
std::cout << "Integers from [1,10]\n";
for (n = 0; n < n_samples; ++n) std::print("{} ", gen.sample(1, 10));
std::print("\n");
std::cout << "Reals from[1,10)\n";
for (n = 0; n < n_samples; ++n) std::print("{:4.2f} ", gen.sample(1., 10.));
std::print("\n");
std::normal_distribution nd{70., 15.};
std::print("Normals with mean {} and std-dev {}\n", nd.mean(), nd.stddev());
for (n = 0; n < n_samples; ++n) std::print("{:4.2f} ", gen.sample(nd));
std::print("\n");
std::binomial_distribution bd{6, 0.5};
std::print("Binomials with {} trials and P[success] = {}\n", bd.t(), bd.p());
for (n = 0; n < n_samples; ++n) std::print("{} ", gen.sample(bd));
std::print("\n");
std::array<int, 10> array;
std::iota(array.begin(), array.end(), 0);
std::print("Random from the array {}\n", array);
for (n = 0; n < n_samples; ++n) std::print("{} ", gen.sample(array));
std::print("\n");
}
Output:
Characters from ['a','z']
'x' 'd' 'n'
Integers from [1,10]
2 10 7
Reals from[1,10)
2.17 3.08 5.18
Normals with mean 70 and std-dev 15
92.30 78.33 66.20
Binomials with 6 trials and P[success] = 0.5
4 1 2
Random from the array [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
2 4 4
Multiple Samples
We provide methods to extract multiple samples without replacement from various sources.
From Containers
We have methods to copy samples from a container without replacement.
template<std::input_iterator Src, typename Dst>
constexpr Dst
1(Src b, Src e, Dst dst, std::unsigned_integral auto n);
sample
template<typename Src, typename Dst>
constexpr auto
2(const Src &src, Dst dst, std::unsigned_integral auto n); sample
- 1
-
Picks
n
elements from the sequence \([b,e)\), without replacement and copies the chosen samples intodst
. Returns the end ofdst
. - 2
-
Picks
n
elements from the containersrc
without replacement and copies the chosen samples intodst
. Returns the end ofdst
.
These methods will preserve the order of the selected elements. You can use the shuffle
method to mix those up.
View std::sample
for more details.
From Distributions
There is also a method to copy samples from an arbitrary distribution into a destination.
template<typename Dst>
constexpr Dst
1(Distribution auto &dist, Dst dst, std::unsigned_integral auto n); sample
- 1
-
Extracts
n
variates from the distributiondist
and puts them intodst
. Returns the end ofdst
.
Examples
Extract multiple samples from a container or a distribution:
#include <utilities/utilities.h>
#include <xoshiro.h>
int main()
{
::rng gen;
xso
constexpr std::size_t N = 10;
constexpr std::size_t K = N / 2;
std::array<int, N> u;
std::iota(u.begin(), u.end(), 0);
std::print("Population: {}\n", u);
std::array<int, K> u_samples;
1.sample(u, u_samples.begin(), u_samples.word_count());
genstd::print("Samples: {}\n", u_samples);
std::normal_distribution nd{70., 15.};
std::array<double, N> v;
2.sample(nd, v.begin(), v.word_count());
genstd::print("Population: {::4.2f}\n", v);
std::array<double, K> v_samples;
3.sample(v, v_samples.begin(), v_samples.word_count());
genstd::print("Samples: {::4.2f}\n", v_samples);
}
- 1
- Generate 5 samples from the population of 10 elements.
- 2
- Generate 10 samples from a normal distribution.
- 3
- Generate 5 samples from that array we just generated.
Output:
Population: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
1Samples: [0, 2, 6, 7, 8]
Population: [66.50, 78.99, 70.24, 58.61, 53.95, 48.18, 74.63, 43.87, 82.07, 60.32]
2Samples: [53.95, 48.18, 43.87, 82.07, 60.32]
- 1
- Note that order is preserved by these samples.
- 2
- That is also true here.