Skip to content

Commit 3dcaef2

Browse files
committed
improve random
1 parent fd7b8e5 commit 3dcaef2

File tree

2 files changed

+66
-22
lines changed

2 files changed

+66
-22
lines changed

include/gf2/core/Random.h

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <limits>
99
#include <random>
10+
#include <type_traits>
1011

1112
#include "Circ.h"
1213
#include "CoreApi.h"
@@ -55,17 +56,36 @@ namespace gf {
5556
}
5657

5758
template<typename T>
58-
T compute_uniform_integer(T min, T max)
59+
std::enable_if_t<std::is_integral_v<T>, T> compute_uniform_integer(T max)
5960
{
60-
std::uniform_int_distribution<T> dist(min, max);
61-
return dist(m_engine);
61+
assert(max > 0);
62+
return static_cast<T>(compute_raw_integer(max));
6263
}
6364

6465
template<typename T>
65-
T compute_uniform_float(T min, T max)
66+
std::enable_if_t<std::is_integral_v<T>, T> compute_uniform_integer(T min, T max)
6667
{
67-
std::uniform_real_distribution<T> dist(min, max);
68-
return dist(m_engine);
68+
assert(min < max);
69+
using U = std::make_unsigned_t<T>;
70+
return min + static_cast<T>(compute_raw_integer(U(max) - U(min)));
71+
}
72+
73+
template<typename T>
74+
std::enable_if_t<std::is_floating_point_v<T>, T> compute_uniform_float()
75+
{
76+
return static_cast<T>(compute_raw_float());
77+
}
78+
79+
template<typename T>
80+
std::enable_if_t<std::is_floating_point_v<T>, T> compute_uniform_float(T max)
81+
{
82+
return static_cast<T>(compute_raw_float() * max);
83+
}
84+
85+
template<typename T>
86+
std::enable_if_t<std::is_floating_point_v<T>, T> compute_uniform_float(T min, T max)
87+
{
88+
return min + static_cast<T>(compute_raw_float() * (max - min));
6989
}
7090

7191
template<typename T>
@@ -98,6 +118,9 @@ namespace gf {
98118
return m_engine;
99119
}
100120

121+
uint64_t compute_raw_integer(uint64_t max);
122+
double compute_raw_float();
123+
101124
private:
102125
RandomEngine m_engine;
103126
};

library/core/Random.cc

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -162,19 +162,15 @@ namespace gf {
162162

163163
Vec2F Random::compute_position(const RectF& area)
164164
{
165-
std::uniform_real_distribution<float> dist_x(area.offset.x, area.offset.x + area.extent.x);
166-
std::uniform_real_distribution<float> dist_y(area.offset.y, area.offset.y + area.extent.y);
167-
const float x = dist_x(m_engine);
168-
const float y = dist_y(m_engine);
165+
const float x = compute_uniform_float(area.offset.x, area.offset.x + area.extent.w);
166+
const float y = compute_uniform_float(area.offset.y, area.offset.y + area.extent.h);
169167
return { x, y };
170168
}
171169

172170
Vec2I Random::compute_position(const RectI& area)
173171
{
174-
std::uniform_int_distribution<int> dist_x(area.offset.x, area.offset.x + area.extent.x - 1);
175-
std::uniform_int_distribution<int> dist_y(area.offset.y, area.offset.y + area.extent.y - 1);
176-
const int x = dist_x(m_engine);
177-
const int y = dist_y(m_engine);
172+
const int x = compute_uniform_integer(area.offset.x, area.offset.x + area.extent.w);
173+
const int y = compute_uniform_integer(area.offset.y, area.offset.y + area.extent.h);
178174
return { x, y };
179175
}
180176

@@ -187,22 +183,47 @@ namespace gf {
187183

188184
float Random::compute_radius(float radius_min, float radius_max)
189185
{
190-
std::uniform_real_distribution<float> dist(gf::square(radius_min), gf::square(radius_max));
191-
return std::sqrt(dist(m_engine));
186+
return std::sqrt(compute_uniform_float(gf::square(radius_min), gf::square(radius_max)));
192187
}
193188

194189
float Random::compute_angle()
195190
{
196-
std::uniform_real_distribution<float> dist(0.0f, 2.0f * gf::Pi);
197-
return dist(m_engine);
191+
return compute_uniform_float(2.0f * gf::Pi);
198192
}
199193

200194
Id Random::compute_id()
201195
{
202-
using Type = std::underlying_type_t<Id>;
203-
const Type lo = std::numeric_limits<Type>::min() + 1;
204-
const Type hi = std::numeric_limits<Type>::max();
205-
return static_cast<Id>(compute_uniform_integer(lo, hi));
196+
uint64_t id = m_engine();
197+
198+
while (id == 0) {
199+
id = m_engine();
200+
}
201+
202+
return Id{ id };
203+
}
204+
205+
// using "Debiased Modulo (Once) — Java's Method" from
206+
// https://www.pcg-random.org/posts/bounded-rands.html
207+
uint64_t Random::compute_raw_integer(uint64_t max)
208+
{
209+
assert(max > 0);
210+
211+
uint64_t x = 0;
212+
uint64_t r = 0;
213+
const uint64_t threshold = std::numeric_limits<uint64_t>::max() - max + 1;
214+
215+
do { // NOLINT(cppcoreguidelines-avoid-do-while)
216+
x = m_engine();
217+
r = x % max;
218+
} while (x - r > threshold);
219+
220+
return r;
221+
}
222+
223+
double Random::compute_raw_float()
224+
{
225+
const uint64_t value = m_engine();
226+
return double(value >> 11) * 0x1.0p-53;
206227
}
207228

208229
} // namespace gf

0 commit comments

Comments
 (0)