header_utils
Loading...
Searching...
No Matches
random.h
1
4
5#pragma once
6
7#include <random>
8#include <concepts>
9#include <numeric>
10#include <span>
11
13{
16
17 thread_local inline std::default_random_engine default_random_engine;
18
19 template <typename INTEGER = uint64_t, typename RANDOM = std::default_random_engine>
21 {
22 static std::uniform_int_distribution<INTEGER> dist;
23 return dist(rng);
24 }
25
26 template <typename REAL = double, typename RANDOM = std::default_random_engine>
28 {
29 static std::uniform_real_distribution<REAL> dist;
30 return dist(rng);
31 }
32
33 template <typename REAL = double, typename RANDOM = std::default_random_engine>
35 {
36 static std::normal_distribution<REAL> dist;
37 return dist(rng);
38 }
39
40 template <typename RANDOM = std::default_random_engine>
42 {
43 if (n_sided < 2) return 0;
44 std::uniform_int_distribution<uint64_t> dist{ 0, n_sided - 1 };
45 return dist(rng) + 1;
46 }
47
48 template <typename RANDOM = std::default_random_engine>
50 {
51 if (n_sided < 2) return 0;
52 std::uniform_int_distribution<uint64_t> dist{ 0, n_sided - 1 };
53 uint64_t sum = 0;
54 for (uint64_t i = 0; i < n_dice; ++i)
55 sum += dist(rng) + 1;
56 return sum;
57 }
58
59 template <uint64_t N_SIDED, typename RANDOM = std::default_random_engine>
60 requires (N_SIDED >= 2)
62 {
63 static std::uniform_int_distribution<uint64_t> dist{ 0, N_SIDED - 1 };
64 return dist(rng) + 1;
65 }
66
67 template <uint64_t N_DICE, uint64_t N_SIDED, typename RANDOM = std::default_random_engine>
68 requires (N_DICE >= 1 && N_SIDED >= 2)
70 {
71 static std::uniform_int_distribution<uint64_t> dist{ 0, N_SIDED - 1 };
72 uint64_t sum = 0;
73 for (uint64_t i = 0; i < N_DICE; ++i)
74 sum += dist(rng) + 1;
75 return sum;
76 }
77
78 template <typename RANDOM = std::default_random_engine>
80 {
81 std::uniform_int_distribution<uint64_t> dist{ 0, 1 };
82 return dist(rng) == 0;
83 }
84
85 namespace operators
86 {
87 inline int operator""_d2(unsigned long long int n) { return (int)dice(n, 2, default_random_engine); }
88 inline int operator""_d4(unsigned long long int n) { return (int)dice(n, 4, default_random_engine); }
89 inline int operator""_d6(unsigned long long int n) { return (int)dice(n, 6, default_random_engine); }
90 inline int operator""_d8(unsigned long long int n) { return (int)dice(n, 8, default_random_engine); }
91 inline int operator""_d10(unsigned long long int n) { return (int)dice(n, 10, default_random_engine); }
92 inline int operator""_d12(unsigned long long int n) { return (int)dice(n, 12, default_random_engine); }
93 inline int operator""_d20(unsigned long long int n) { return (int)dice(n, 20, default_random_engine); }
94 inline int operator""_d100(unsigned long long int n) { return (int)dice(n, 100, default_random_engine); }
95 }
96
97 template <class T, class... TYPES>
98 constexpr inline bool is_any_of_v = std::disjunction_v<std::is_same<T, TYPES>...>;
99
100 template <typename RANDOM = std::default_random_engine, typename T>
102 T in_integer_range(T from, T to, RANDOM& rng = ::ghassanpl::random::default_random_engine)
103 {
104 if (from >= to) return T{};
105 std::uniform_int_distribution<T> dist{ from, to };
106 return dist(rng);
107 }
108
109 template <typename RANDOM = std::default_random_engine, std::floating_point T>
110 T in_real_range(T from, T to, RANDOM& rng = ::ghassanpl::random::default_random_engine)
111 {
112 if (from >= to) return T{};
113 std::uniform_real_distribution<T> dist{ from, to };
114 return dist(rng);
115 }
116
117 template <typename RANDOM = std::default_random_engine, typename T>
118 auto in_range(T from, T to, RANDOM& rng = ::ghassanpl::random::default_random_engine)
119 {
120 if (from >= to) return T{};
121
122 if constexpr (std::is_enum_v<T>)
123 {
124 std::uniform_int_distribution<std::underlying_type_t<T>> dist(from, to);
125 return (T)dist(rng);
126 }
127 else if constexpr (std::is_floating_point_v<T>)
128 return in_real_range(from, to, rng);
130 return in_integer_range(from, to, rng);
132 {
135 using signed_as_T = std::conditional_t<std::is_signed_v<T>, int64_t, uint64_t>;
136 return static_cast<T>(in_integer_range(static_cast<signed_as_T>(from), static_cast<signed_as_T>(to), rng));
137 }
138 else
139 {
140 const auto i = to - from;
141 const auto r = ::ghassanpl::random::in_range(decltype(i){}, i);
142 return from + r;
143 }
144 }
145
147
148 template <std::floating_point T = float>
149 constexpr T halton_sequence(size_t index, size_t base = 2)
150 {
151 auto result = T(0);
152 auto fraction = T(1);
153 while (index > 0)
154 {
155 fraction /= base;
156 result += fraction * (index % base);
157 index /= base;
158 }
159 return result;
160 }
161
162 template <typename RANDOM = std::default_random_engine>
163 bool with_probability(double probability, RANDOM& rng = ::ghassanpl::random::default_random_engine)
164 {
165 return percentage(rng) < std::clamp(probability, 0.0, 1.0);
166 }
167
168 template <typename RANDOM = std::default_random_engine>
169 bool with_probability(double probability, double& result, RANDOM& rng = ::ghassanpl::random::default_random_engine)
170 {
171 auto res = result = percentage(rng);
172 return res < std::clamp(probability, 0.0, 1.0);
173 }
174
175 template <typename RANDOM = std::default_random_engine>
177 {
178 if (n == 0) return false;
179 return with_probability(1.0 / double(n), rng);
180 }
181
182 template <typename RANDOM = std::default_random_engine, typename T>
184 {
185 using std::begin;
186 using std::end;
187 std::shuffle(begin(cont), end(cont), rng);
188 }
189
190 template <typename RANDOM = std::default_random_engine, typename T>
191 requires std::ranges::sized_range<T>
193 {
194 using std::size;
195 using std::begin;
196 return begin(cont) + in_integer_range(0LL, (int64_t)size(cont) - 1, rng);
197 }
198
199 template <typename RANDOM = std::default_random_engine, typename T, typename PRED>
200 requires std::ranges::sized_range<T>
202 {
203 using std::size;
204 using std::begin;
205 using std::end;
206 auto begin_it = begin(cont);
207 const auto end_it = end(cont);
208 const auto valid_count = std::count_if(begin_it, end_it, pred);
209 auto item_position = in_integer_range(0LL, (int64_t)valid_count - 1, rng);
210 for (; begin_it != end_it; ++begin_it)
211 {
212 if (pred(*begin_it) && (item_position--) == 0)
213 {
214 return begin_it;
215 }
216 }
217 return end_it;
218 }
219
220 template <typename RANDOM = std::default_random_engine, typename T>
222 {
223 return std::distance(begin(cont), iterator(cont, rng));
224 }
225
226 template <typename RANDOM = std::default_random_engine, typename T, typename PRED>
228 {
229 return std::distance(begin(cont), iterator_if(cont, std::forward<PRED>(pred), rng));
230 }
231
232 template <typename RANDOM = std::default_random_engine, typename T>
234 {
235 using std::end;
236 auto result = iterator(cont, rng);
237 return (result != end(cont)) ? std::to_address(result) : nullptr;
238 }
239
240 template <typename RANDOM = std::default_random_engine, typename T, typename PRED>
242 {
243 using std::end;
244 auto result = iterator_if(cont, std::forward<PRED>(predicate), rng);
245 return (result != end(cont)) ? std::to_address(result) : nullptr;
246 }
247
248 template <typename... T>
249 auto one_of(T&&... values)
250 {
252 static_assert(sizeof...(values) > 0, "at least one value must be provided");
253 auto v = std::array{ std::forward<T>(values)... };
254 return std::move(*element(v, ::ghassanpl::random::default_random_engine));
255 }
256
257 template <typename RANDOM = std::default_random_engine, typename T>
258 auto one_of(std::initializer_list<T> values, RANDOM& rng = ::ghassanpl::random::default_random_engine)
259 {
260 if (values.size() == 0) throw std::invalid_argument("values");
261 return *element(values, rng);
262 }
263
267
268
269 template <typename RANDOM = std::default_random_engine, typename T>
271 {
272 using Iterator = decltype(std::end(container));
273 struct Randomizer
274 {
275 Randomizer(RANDOM& rng, T& container)
276 : mRNG(rng)
277 {
278 for (auto it = std::begin(container); it != std::end(container); ++it)
279 mIterators.push_back(it);
280 mCurrent = mIterators.end();
281 }
282 auto Next() { if (mCurrent == mIterators.end()) { Shuffle(); } return *mCurrent++; }
283 void Shuffle() { std::ranges::shuffle(mIterators, mRNG); mCurrent = mIterators.begin(); }
284 private:
285 RANDOM& mRNG;
286 std::vector<Iterator> mIterators;
287 typename std::vector<Iterator>::iterator mCurrent;
288 };
289
290 return Randomizer{ rng, container };
291 }
292
296 template <std::convertible_to<double> T, typename RANDOM>
298 {
300 std::discrete_distribution<size_t> dist(option_probabilities.begin(), option_probabilities.end());
301 return dist(rng);
302 }
303
307 template <typename RANGE, typename FUNC, typename RANDOM>
309 {
310 static_assert(std::ranges::forward_range<RANGE>, "range must be forward range");
311 static_assert(std::is_invocable_v<FUNC, std::ranges::range_reference_t<RANGE>>);
312 static_assert(std::convertible_to<std::invoke_result_t<FUNC, std::ranges::range_reference_t<RANGE>>, double>);
313
314 const auto sum = std::accumulate(std::ranges::begin(range), std::ranges::end(range), 0.0, [&](auto acc, auto&& item) { return acc + (double)prob_func(item); });
315 auto target = in_real_range(0.0, sum, rng);
316
317 for (auto it = std::ranges::begin(range); it != std::ranges::end(range); ++it)
318 {
319 const auto weight = (double)prob_func(*it);
320 if (target < weight)
321 return it;
322 target = target - weight;
323 }
324
325 return std::ranges::end(range);
326 }
327
328}
constexpr auto bit_count
Equal to the number of bits in the type.
Definition bits.h:33
constexpr CONTAINER to(RANGE &&range, TYPES &&... args)
to<container>();
Definition ranges.h:369
constexpr T halton_sequence(size_t index, size_t base=2)
TODO: Should we move the below to random_seq?
Definition random.h:149
size_t option_with_probability(std::span< T const > option_probabilities, RANDOM &rng=::ghassanpl::random::default_random_engine)
When probability calculations are known ahead of time or expensive \complexity O(N) space,...
Definition random.h:297
auto iterator_with_probability(RANGE &&range, FUNC &&prob_func, RANDOM &rng=::ghassanpl::random::default_random_engine)
For cheap probability functions.
Definition random.h:308
auto make_bag_randomizer(T &container, RANDOM &rng=::ghassanpl::random::default_random_engine)
TODO: random_range()
Definition random.h:270
thread_local std::default_random_engine default_random_engine
TODO: Tests check out https://github.com/effolkronium/random/.
Definition random.h:17