Iros
 
Loading...
Searching...
No Matches
uniform_int_distribution.h
Go to the documentation of this file.
1#pragma once
2
4#include "di/meta/common.h"
5#include "di/meta/language.h"
7
8namespace di::random {
9template<concepts::Integer T = int>
11public:
12 using Result = T;
13 class Param {
14 public:
16
17 constexpr Param() : Param(0) {}
18 constexpr explicit Param(T a, T b = math::NumericLimits<T>::max) : m_a(a), m_b(b) {}
19
20 constexpr auto a() const -> T { return m_a; }
21 constexpr auto b() const -> T { return m_b; }
22
23 private:
24 constexpr friend auto operator==(Param const& a, Param const& b) -> bool {
25 return a.a() == b.a() && a.b() == b.b();
26 }
27
28 T m_a { 0 };
29 T m_b { 0 };
30 };
31
33 constexpr explicit UniformIntDistribution(T a, T b = math::NumericLimits<T>::max) : m_param(a, b) {}
34 constexpr explicit UniformIntDistribution(Param const& param) : m_param(param) {}
35
36 constexpr void reset() {}
37
38 template<typename Gen>
40 constexpr auto operator()(Gen&& generator) const -> T {
41 return (*this)(generator, param());
42 }
43
44 template<typename Gen>
46 constexpr auto operator()(Gen&& generator, Param const& param) const -> T {
48
49 // TODO: adopt a more sophisticated approach for implementing this function
50 // which does not naively retry the number generation on out of bounds.
51 // See 2018 paper: Fast Random Integer Generation in an Interval
52 // DANIEL LEMIRE, Université du Québec (TELUQ), Canada
53 constexpr U generated_min = meta::RemoveReference<Gen>::min();
54 constexpr U generated_max = meta::RemoveReference<Gen>::max();
55 constexpr U generated_range = generated_max - generated_min;
56
57 // A general simplification of generating an integer in the range
58 // [a, b] is to consider generating an integer from [0, b - a] instead,
59 // making sure to add a back in the end. When sampling from the generating
60 // range, all that matters is the size of its output range, and so generated
61 // values are always normalized by subtracting the generator's minimum value.
62 auto generate = [&] -> U {
63 return generator() - generated_min;
64 };
65
66 U a = param.a();
67 U b = param.b();
68 U desired_range = b - a;
69
70 if (generated_range == desired_range) {
71 // Simply sample the generated range, since it has the same
72 // size as the desired range.
73 return a + generate();
74 }
75
76 if (generated_range > desired_range) {
77 // Down sample the generated bits.
78 // Imagine having a generated range [0, 15], and a desired range [0, 2].
79 // To down sample the generated range, note that the desired range has 3
80 // elements, and the generated range has 16. Therefore, generate a random value
81 // and discard it if it has the value 15. Otherwise, dividing by 5 will give
82 // a properly sampled integer.
83 U const desired_size = desired_range + 1;
84 U const scale_factor = generated_range / desired_size;
85 U const out_of_bounds = desired_size * scale_factor;
86 for (;;) {
87 auto result = generate();
88 if (result >= out_of_bounds) {
89 continue;
90 }
91 return a + result / scale_factor;
92 }
93 } else {
94 // Up sample the generated bits.
95 // Imagine having a generated range [0, 3], and a desired range [0, 12].
96 // To up sample the generated range, realize that the 13 element space in
97 // the desired range can be partitioned into 4 parts, one for each possible
98 // outcome of the generated range. So, recusively generate a uniform integer in
99 // the range [0, 3] and multiply that by 4 to get an index in { 0, 4, 8, 12 }.
100 // Then add to this index a non-scaled generated value, to produce a uniform
101 // integer in the range [0, 15]. And finally, discard the value if it is out of bounds.
102 auto const generated_size = U(generated_range + 1);
103 for (;;) {
104 U const base_index = generated_size * (*this)(generator, Param(0U, desired_range / generated_size));
105 U const index = generate();
106 U const result = base_index + index;
107
108 // Try again result is out of bounds (this also checks if the computation overflowed).
109 if (result > desired_range || result < base_index) {
110 continue;
111 }
112 return a + result;
113 }
114 }
115 }
116
117 constexpr auto a() const -> T { return m_param.a(); }
118 constexpr auto b() const -> T { return m_param.b(); }
119
120 constexpr auto param() const -> Param { return m_param; }
121 constexpr void param(Param const& param) const { m_param = param; }
122
123 constexpr auto min() const -> T { return a(); }
124 constexpr auto max() const -> T { return b(); }
125
126private:
127 constexpr friend auto operator==(UniformIntDistribution const& a, UniformIntDistribution const& b) -> bool {
128 return a.param() == b.param();
129 }
130
131 Param m_param {};
132};
133}
134
135namespace di {
137}
Definition uniform_int_distribution.h:13
constexpr Param(T a, T b=math::NumericLimits< T >::max)
Definition uniform_int_distribution.h:18
constexpr auto b() const -> T
Definition uniform_int_distribution.h:21
constexpr Param()
Definition uniform_int_distribution.h:17
constexpr friend auto operator==(Param const &a, Param const &b) -> bool
Definition uniform_int_distribution.h:24
UniformIntDistribution Distribution
Definition uniform_int_distribution.h:15
constexpr auto a() const -> T
Definition uniform_int_distribution.h:20
Definition uniform_int_distribution.h:10
constexpr UniformIntDistribution(Param const &param)
Definition uniform_int_distribution.h:34
constexpr auto param() const -> Param
Definition uniform_int_distribution.h:120
constexpr friend auto operator==(UniformIntDistribution const &a, UniformIntDistribution const &b) -> bool
Definition uniform_int_distribution.h:127
constexpr void param(Param const &param) const
Definition uniform_int_distribution.h:121
constexpr UniformIntDistribution(T a, T b=math::NumericLimits< T >::max)
Definition uniform_int_distribution.h:33
T Result
Definition uniform_int_distribution.h:12
constexpr auto a() const -> T
Definition uniform_int_distribution.h:117
constexpr void reset()
Definition uniform_int_distribution.h:36
constexpr UniformIntDistribution()
Definition uniform_int_distribution.h:32
constexpr auto min() const -> T
Definition uniform_int_distribution.h:123
constexpr auto b() const -> T
Definition uniform_int_distribution.h:118
constexpr auto max() const -> T
Definition uniform_int_distribution.h:124
Definition uniform_random_bit_generator.h:10
detail::CommonTypeHelper< Types... >::Type CommonType
Definition common.h:62
Type< detail::RemoveReferenceHelper< T > > RemoveReference
Definition core.h:71
Definition uniform_int_distribution.h:8
Definition zstring_parser.h:9
Definition numeric_limits.h:7