Skip to content

Commit 1586938

Browse files
Add QuickInteger distribution
1 parent 610a233 commit 1586938

File tree

10 files changed

+918
-758
lines changed

10 files changed

+918
-758
lines changed

docs/random.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,30 @@ inclusive; the bounds will be swapped if they are in the wrong order.
151151
_TODO: The current implementation exhibits undefined behaviour if the output
152152
range is larger than that of the input PRNG._
153153

154+
### Quick uniform integer distribution
155+
156+
```c++
157+
template <Integral T>
158+
class QuickRandom {
159+
using result_type = T;
160+
constexpr QuickRandom() noexcept;
161+
constexpr explicit QuickRandom(T range) noexcept;
162+
constexpr explicit QuickRandom(T min, T max) noexcept;
163+
template <std::uniform_random_bit_generator RNG>
164+
constexpr T operator()(RNG& rng) const;
165+
constexpr T min() const noexcept;
166+
constexpr T max() const noexcept;
167+
};
168+
```
169+
170+
This has the same interface as `UniformInteger,` but generates its result with
171+
a simple modulo operation, and will never call the underlying RNG more than
172+
once. This is faster than `UniformInteger` but has a small bias, on the order
173+
of `output-range/RNG-range,` if the output range is not a power of 2.
174+
175+
_TODO: This has the same issue as `UniformInteger` if the output range is
176+
large._
177+
154178
### Uniform floating point distribution
155179
156180
```c++

src/CMakeLists.txt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.30)
66
# version.hpp.
77

88
project(rs-core
9-
VERSION 0.1.86
9+
VERSION 0.1.87
1010
LANGUAGES CXX
1111
)
1212

@@ -60,7 +60,10 @@ add_executable(${unittest}
6060
test/mp-integer-signed-conversion-test.cpp
6161
test/mp-integer-unsigned-arithmetic-test.cpp
6262
test/mp-integer-unsigned-conversion-test.cpp
63-
test/random-test.cpp
63+
test/random-algorithm-test.cpp
64+
test/random-engine-test.cpp
65+
test/random-float-test.cpp
66+
test/random-integer-test.cpp
6467
test/rational-int-test.cpp
6568
test/rational-mp-integer-basics-test.cpp
6669
test/rational-mp-integer-maths-addition-subtraction-test.cpp

src/rs-core/random.hpp

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ namespace RS {
176176
private:
177177

178178
T min_{0};
179-
std::uint64_t range_{0};
179+
std::uint64_t range_{0}; // 0 if range is full uint64_t
180180
std::uint64_t threshold_{0}; // Highest multiple of range that fits in a uint64_t
181181

182182
constexpr void update() noexcept;
@@ -222,6 +222,11 @@ namespace RS {
222222

223223
template <Integral T>
224224
constexpr T UniformInteger<T>::max() const noexcept {
225+
if constexpr (std::same_as<T, std::uint64_t>) {
226+
if (range_ == 0) {
227+
return std::numeric_limits<std::uint64_t>::max();
228+
}
229+
}
225230
auto half_range = range_ / 2;
226231
auto t1 = static_cast<T>(half_range);
227232
auto t2 = static_cast<T>(range_ - half_range - 1);
@@ -237,6 +242,74 @@ namespace RS {
237242
}
238243
}
239244

245+
// Quick integer distribution
246+
247+
template <Integral T>
248+
class QuickRandom {
249+
250+
public:
251+
252+
using result_type = T;
253+
254+
constexpr QuickRandom() noexcept;
255+
constexpr explicit QuickRandom(T range) noexcept; // UB if range<=0
256+
constexpr explicit QuickRandom(T min, T max) noexcept;
257+
258+
template <std::uniform_random_bit_generator RNG>
259+
constexpr T operator()(RNG& rng) const;
260+
261+
constexpr T min() const noexcept { return min_; }
262+
constexpr T max() const noexcept;
263+
264+
private:
265+
266+
T min_{0};
267+
std::uint64_t range_{0}; // 0 if range is full uint64_t
268+
269+
};
270+
271+
template <Integral T>
272+
constexpr QuickRandom<T>::QuickRandom() noexcept:
273+
min_(0),
274+
range_(static_cast<std::uint64_t>(std::numeric_limits<T>::max()) + 1) {}
275+
276+
template <Integral T>
277+
constexpr QuickRandom<T>::QuickRandom(T range) noexcept:
278+
min_(0),
279+
range_(static_cast<std::uint64_t>(range)) {}
280+
281+
template <Integral T>
282+
constexpr QuickRandom<T>::QuickRandom(T min, T max) noexcept {
283+
if (min > max) {
284+
std::swap(min, max);
285+
}
286+
min_ = min;
287+
range_ = static_cast<std::uint64_t>(max - min) + 1;
288+
}
289+
290+
template <Integral T>
291+
template <std::uniform_random_bit_generator RNG>
292+
constexpr T QuickRandom<T>::operator()(RNG& rng) const {
293+
auto x = rng();
294+
if (range_ != 0) {
295+
x %= range_;
296+
}
297+
return min_ + static_cast<T>(x);
298+
}
299+
300+
template <Integral T>
301+
constexpr T QuickRandom<T>::max() const noexcept {
302+
if constexpr (std::same_as<T, std::uint64_t>) {
303+
if (range_ == 0) {
304+
return std::numeric_limits<std::uint64_t>::max();
305+
}
306+
}
307+
auto half_range = range_ / 2;
308+
auto t1 = static_cast<T>(half_range);
309+
auto t2 = static_cast<T>(range_ - half_range - 1);
310+
return min_ + t1 + t2;
311+
}
312+
240313
// Bernoulli distribution
241314

242315
class BernoulliDistribution {

src/rs-core/version.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@
88
namespace RS {
99

1010
inline std::array<int, 3> version() noexcept {
11-
return {{ 0, 1, 86 }};
11+
return {{ 0, 1, 87 }};
1212
}
1313

1414
inline std::string version_string() {
15-
return "0.1.86";
15+
return "0.1.87";
1616
}
1717

1818
}

0 commit comments

Comments
 (0)