aarand
Aaron's random distribution functions
Loading...
Searching...
No Matches
aarand.hpp
Go to the documentation of this file.
1#ifndef AARAND_AARAND_HPP
2#define AARAND_AARAND_HPP
3
4#include <cmath>
5#include <limits>
6#include <stdexcept>
7#include <type_traits>
8
19namespace aarand {
20
31template<typename T = double, class Engine>
32T standard_uniform(Engine& eng) {
33 typedef typename Engine::result_type R;
34 static_assert(std::numeric_limits<R>::is_integer, "RNG engine must yield integer results");
35
36 // Can't be bothered to figure out whether the range fits into 'R' for signed values.
37 // So instead, we just require unsigned integers, where the range will always fit.
38 static_assert(!std::numeric_limits<R>::is_signed, "RNG engine must yield unsigned integers");
39
40 // Make sure we get the right type to avoid inadvertent promotions.
41 constexpr T ONE_ = 1;
42
43 // Stolen from Boost, see https://www.boost.org/doc/libs/1_67_0/boost/random/uniform_01.hpp
44 // The +1 probably doesn't matter for 64-bit generators, but is helpful for engines with
45 // fewer output bits, to reduce the (small) probability of sampling 1's.
46 constexpr T factor = ONE_ / (static_cast<T>(Engine::max() - Engine::min()) + ONE_);
47
48 // Note that it still might be possible to get a result = 1, depending on
49 // the numerical precision used to compute the product; hence the loop.
50 T result;
51 do {
52 result = static_cast<T>(eng() - Engine::min()) * factor;
53 } while (result == ONE_);
54
55 return result;
56}
57
68template<typename T = double, class Engine>
69std::pair<T, T> standard_normal(Engine& eng) {
70 constexpr T PI_ = 3.14159265358979323846;
71 constexpr T TWO_ = 2;
72
73 // Box-Muller gives us two random values at a time.
74 T constant = std::sqrt(-TWO_ * std::log(standard_uniform<T>(eng)));
76 return std::make_pair(constant * std::sin(angle), constant * std::cos(angle));
77}
78
89template<typename T = double, class Engine>
91 T val;
92 do {
94 } while (val == static_cast<T>(0));
95 return -std::log(val);
96}
97
108template<typename T = int, class Engine>
110 static_assert(std::numeric_limits<T>::is_integer);
111 if (bound <= 0) {
112 throw std::runtime_error("'bound' should be a positive integer");
113 }
114
115 typedef typename Engine::result_type R;
116 static_assert(std::numeric_limits<R>::is_integer);
117 static_assert(!std::numeric_limits<R>::is_signed); // don't want to figure out how to store the range if it might not fit into R.
118
119 constexpr R range = Engine::max() - Engine::min();
120 if (static_cast<typename std::make_unsigned<T>::type>(bound) > range) { // force an unsigned comparison.
121 throw std::runtime_error("'bound' should be less than the RNG range");
122 }
123
124 R draw = eng() - Engine::min();
125
126 // Conservative shortcut to avoid an extra modulo operation in computing
127 // 'limit' if 'draw' is below 'limit'. This is based on the observation
128 // that 'range - bound <= limit', so any condition that triggers the loop
129 // will also pass this check. Allows early return when 'range >> bound'.
130 if (draw > range - bound) {
131
132 // The limit is necessary to provide uniformity in the presence of the
133 // modulus. The idea is to re-sample if we get a draw above the limit.
134 // Technically this can have problems as bound approaches range, in which
135 // case we might end up discarding a lot of the sample space... but this
136 // is unlikely to happen in practice, and even if it does, it's a rejection
137 // rate that's guaranteed to be less than 50%, so whatever.
138 //
139 // Note that the +1 is necessary because range is inclusive but bound is not.
140 const R limit = range - ((range % bound) + 1);
141
142 // In addition, we don't have to deal with the crap about combining draws
143 // to get enough entropy, which is 90% of the Boost implementation.
144 while (draw > limit) {
145 draw = eng() - Engine::min();
146 }
147 }
148
149 return draw % bound;
150}
151
162template<class In, class Engine>
163void shuffle(In values, size_t n, Engine& eng) {
164 if (n) {
165 using std::swap;
166 for (size_t i = 0; i < n - 1; ++i) {
167 auto chosen = discrete_uniform(eng, n - i);
168 swap(*(values + i), *(values + i + chosen));
169 }
170 }
171 return;
172}
173
188template<class In, class Out, class Engine>
189void sample(In values, size_t n, size_t s, Out output, Engine& eng) {
190 for (size_t i = 0; i < n && s; ++i, ++values) {
191 const double threshold = static_cast<double>(s)/(n - i);
192 if (threshold >= 1 || standard_uniform(eng) <= threshold) {
193 *output = *values;
194 ++output;
195 --s;
196 }
197 }
198}
199
213template<class Out, class Engine>
214void sample(size_t bound, size_t s, Out output, Engine& eng) {
215 for (size_t i = 0; i < bound && s; ++i) {
216 const double threshold = static_cast<double>(s)/(bound - i);
217 if (threshold >= 1 || standard_uniform(eng) <= threshold) {
218 *output = i;
219 ++output;
220 --s;
221 }
222 }
223}
224
225}
226
227#endif
Namespace containing Aaron's random distribution functions.
T standard_exponential(Engine &eng)
Definition aarand.hpp:90
std::pair< T, T > standard_normal(Engine &eng)
Definition aarand.hpp:69
void shuffle(In values, size_t n, Engine &eng)
Definition aarand.hpp:163
T standard_uniform(Engine &eng)
Definition aarand.hpp:32
void sample(In values, size_t n, size_t s, Out output, Engine &eng)
Definition aarand.hpp:189
T discrete_uniform(Engine &eng, T bound)
Definition aarand.hpp:109