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 containercwhich 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 embeddedparam_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
diste.g. from astd::normal_distributioninstance.
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()
{
xso::rng gen;
std::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 4Multiple 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
1sample(Src b, Src e, Dst dst, std::unsigned_integral auto n);
template<typename Src, typename Dst>
constexpr auto
2sample(const Src &src, Dst dst, std::unsigned_integral auto n);- 1
-
Picks
nelements from the sequence \([b,e)\), without replacement and copies the chosen samples intodst. Returns the end ofdst. - 2
-
Picks
nelements from the containersrcwithout 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
1sample(Distribution auto &dist, Dst dst, std::unsigned_integral auto n);- 1
-
Extracts
nvariates from the distributiondistand 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()
{
xso::rng gen;
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 gen.sample(u, u_samples.begin(), u_samples.word_count());
std::print("Samples: {}\n", u_samples);
std::normal_distribution nd{70., 15.};
std::array<double, N> v;
2 gen.sample(nd, v.begin(), v.word_count());
std::print("Population: {::4.2f}\n", v);
std::array<double, K> v_samples;
3 gen.sample(v, v_samples.begin(), v_samples.word_count());
std::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.