24 struct good_random_engine
27 good_random_engine(uint64_t j)
noexcept {
31 void seed(uint64_t j)
noexcept
33 v = 4101842887655102017LL;
35 u = j ^ v; (void)this->
operator()();
36 v = u; (void)this->
operator()();
37 w = v; (void)this->
operator()();
40 [[nodiscard]]
static constexpr uint64_t max()
noexcept {
return UINT64_MAX; }
41 [[nodiscard]]
static constexpr uint64_t min()
noexcept {
return 0; }
43 [[nodiscard]] uint64_t operator()()
noexcept
45 u = u * 2862933555777941757LL + 7046029254386353087LL;
46 v ^= v >> 17; v ^= v << 31; v ^= v >> 8;
47 w = 4294957665U * (w & 0xffffffff) + (w >> 32);
48 uint64_t x = u ^ (u << 21); x ^= x >> 35; x ^= x << 4;
54 static_assert(std::uniform_random_bit_generator<good_random_engine>);
61 template <
typename INTEGER = u
int64_t,
typename RANDOM = std::default_random_engine>
64 std::uniform_int_distribution<INTEGER> dist;
69 template <
typename REAL =
double,
typename RANDOM = std::default_random_engine>
72 std::uniform_real_distribution<REAL> dist;
89 template <
typename REAL =
double,
typename RANDOM = std::default_random_engine>
92 static std::normal_distribution<REAL> dist;
97 template <
typename RANDOM = std::default_random_engine>
100 if (n_sided < 2)
return 0;
101 std::uniform_int_distribution<uint64_t> dist{ 0, n_sided - 1 };
102 return dist(rng) + 1;
106 template <
typename RANDOM = std::default_random_engine>
109 if (n_sided < 2 || n_dice == 0)
return 0;
110 std::uniform_int_distribution<uint64_t> dist{ 0, n_sided - 1 };
112 for (uint64_t i = 0; i < n_dice; ++i)
113 sum += dist(rng) + 1;
118 template <u
int64_t N_SIDED,
typename RANDOM = std::default_random_engine>
121 static_assert(N_SIDED >= 2,
"you cannot roll a 0 or 1-sided die");
122 static std::uniform_int_distribution<uint64_t> dist{ 0, N_SIDED - 1 };
123 return dist(rng) + 1;
127 template <u
int64_t N_DICE, u
int64_t N_SIDED,
typename RANDOM = std::default_random_engine>
130 static_assert(N_DICE >= 1 ,
"you cannot roll fewer than one die");
131 static_assert(N_SIDED >= 2,
"you cannot roll a 0 or 1-sided die");
132 static std::uniform_int_distribution<uint64_t> dist{ 0, N_SIDED - 1 };
134 for (uint64_t i = 0; i < N_DICE; ++i)
135 sum += dist(rng) + 1;
140 template <
typename RANDOM = std::default_random_engine>
143 std::uniform_int_distribution<uint64_t> dist{ 0, 1 };
144 return dist(rng) == 0;
166 template <
typename RANDOM = std::default_random_engine, std::
integral T>
169 static_assert(is_any_of_v<T, short, int, long, long long, unsigned short, unsigned int, unsigned long, unsigned long long>,
"in_integer_range only works on real integer types");
170 if (from >=
to)
return T{};
171 std::uniform_int_distribution<T> dist{ from,
to };
175 template <
typename RANDOM = std::default_random_engine, std::
integral T>
178 return in_integer_range(T{},
to, rng);
181 template <
typename RANDOM = std::default_random_engine, std::
floating_po
int T>
184 static_assert(std::is_floating_point_v<T>,
"in_real_range only works on floating point types");
185 if (from >=
to)
return T{};
186 std::uniform_real_distribution<T> dist{ from,
to };
190 template <
typename RANDOM = std::default_random_engine, std::
floating_po
int T>
193 return in_real_range(T{},
to, rng);
196 template <
typename RANDOM = std::default_random_engine,
typename T>
199 if constexpr (std::is_enum_v<T>)
201 if (from >=
to)
return T{};
203 std::uniform_int_distribution<std::underlying_type_t<T>> dist(from,
to);
206 else if constexpr (std::is_floating_point_v<T>)
208 if (from >=
to)
return T{};
209 return in_real_range(from,
to, rng);
211 else if constexpr (is_any_of_v<T, short, int, long, long long, unsigned short, unsigned int, unsigned long, unsigned long long>)
213 if (from >=
to)
return T{};
214 return in_integer_range(from,
to, rng);
217 is_any_of_v<T, unsigned char, signed char, char, wchar_t, char16_t, char32_t>
219 || std::is_same_v<T, char8_t>
223 if (from >=
to)
return T{};
226 using signed_as_T = std::conditional_t<std::is_signed_v<T>, int64_t, uint64_t>;
227 return static_cast<T
>(in_integer_range(
static_cast<signed_as_T
>(from),
static_cast<signed_as_T
>(
to), rng));
249 template <
typename T =
float>
250 [[nodiscard]]
constexpr T halton_sequence(
size_t index,
size_t base = 2)
252 static_assert(std::is_floating_point_v<T>,
"halton_sequence only works on floating point types");
255 auto fraction = T(1);
259 result += fraction * (
index % base);
266 template <
typename RANDOM = std::default_random_engine>
269 return percentage(rng) < std::clamp(probability, 0.0, 1.0);
273 template <
typename RANDOM = std::default_random_engine>
277 return res < std::clamp(probability, 0.0, 1.0);
281 template <
typename RANDOM = std::default_random_engine>
284 if (n == 0)
return false;
289 template <
typename RANDOM = std::default_random_engine,
typename T>
294 std::shuffle(begin(cont), end(cont), rng);
298 template <
typename RANDOM = std::default_random_engine,
typename T>
299 requires std::ranges::sized_range<T>
304 return begin(cont) + in_integer_range(0LL, (int64_t)size(cont) - 1, rng);
308 template <
typename RANDOM = std::default_random_engine,
typename T,
typename PRED>
309 requires std::ranges::sized_range<T>
315 auto begin_it = begin(cont);
316 const auto end_it = end(cont);
317 const auto valid_count = std::count_if(begin_it, end_it,
pred);
318 auto item_position = in_integer_range(0LL, (int64_t)valid_count - 1, rng);
319 for (; begin_it != end_it; ++begin_it)
321 if (
pred(*begin_it) && (item_position--) == 0)
330 template <
typename RANDOM = std::default_random_engine,
typename T>
333 return std::distance(begin(cont),
iterator(cont, rng));
337 template <
typename RANDOM = std::default_random_engine,
typename T,
typename PRED>
340 return std::distance(begin(cont),
iterator_if(cont, std::forward<PRED>(
pred), rng));
344 template <
typename RANDOM = std::default_random_engine,
typename T>
349 return (result != end(cont)) ? std::addressof(*result) :
nullptr;
353 template <
typename RANDOM = std::default_random_engine,
typename T,
typename PRED>
358 return (result != end(cont)) ? std::addressof(*result) :
nullptr;
362 template <
typename... T>
366 static_assert(
sizeof...(values) > 0,
"at least one value must be provided");
367 auto v = std::array{ std::forward<T>(values)... };
372 template <
typename RANDOM = std::default_random_engine,
typename T>
375 if (values.size() == 0)
throw std::invalid_argument(
"values");
386 template <
typename RANDOM = std::default_random_engine,
typename T>
389 using Iterator =
decltype(std::begin(container));
392 randomizer_t(RANDOM& rng, T& container)
395 for (
auto it = std::begin(container); it != std::end(container); ++it)
396 mIterators.push_back(it);
397 mCurrent = mIterators.end();
399 decltype(
auto) next() {
if (mCurrent == mIterators.end()) {
shuffle(); }
return *mCurrent++; }
400 void shuffle() { std::shuffle(mIterators.begin(), mIterators.end(), mRNG); mCurrent = mIterators.begin(); }
403 std::vector<Iterator> mIterators;
404 typename std::vector<Iterator>::iterator mCurrent;
407 return randomizer_t{ rng, container };
412 template <
typename T,
typename RANDOM>
416 static_assert(std::is_convertible_v<T, double>,
"option probabilities must be convertible to double");
418 std::discrete_distribution<size_t> dist(option_probabilities.begin(), option_probabilities.end());
426 template <
typename RANGE,
typename FUNC,
typename RANDOM>
429#ifdef __cpp_lib_ranges
430 static_assert(std::ranges::forward_range<RANGE>,
"range must be forward range");
431 static_assert(std::is_invocable_v<FUNC, std::ranges::range_reference_t<RANGE>>);
432 static_assert(std::convertible_to<std::invoke_result_t<FUNC, std::ranges::range_reference_t<RANGE>>,
double>);
435 const auto start = std::begin(range);
436 const auto end = std::end(range);
438 const auto sum = std::accumulate(start, end, 0.0, [&](
auto acc,
auto&& item) {
return acc + (double)prob_func(item); });
439 auto target = in_real_range(0.0, sum, rng);
441 for (
auto it = start; it != end; ++it)
443 const auto weight = (double)prob_func(*it);
446 target = target - weight;