|
|
@@ -16,7 +16,6 @@ Example: |
|
|
|
std::random_device rd; |
|
|
|
random::Xoroshiro128Plus rng(rd(), rd()); |
|
|
|
uint64_t r = rng(); |
|
|
|
uint32_t r = rng.u32(); |
|
|
|
|
|
|
|
std::uniform_real_distribution<float> uniform(0.f, 1.f); |
|
|
|
float r = uniform(rng); |
|
|
@@ -59,31 +58,10 @@ struct Xoroshiro128Plus { |
|
|
|
constexpr uint64_t max() const { |
|
|
|
return UINT64_MAX; |
|
|
|
} |
|
|
|
|
|
|
|
uint64_t u64() { |
|
|
|
return operator()(); |
|
|
|
} |
|
|
|
uint64_t u32() { |
|
|
|
// Take top 32 bits which has better randomness properties. |
|
|
|
return u64() >> 32; |
|
|
|
} |
|
|
|
uint16_t u16() { |
|
|
|
return u64() >> 48; |
|
|
|
} |
|
|
|
uint8_t u8() { |
|
|
|
return u64() >> 56; |
|
|
|
} |
|
|
|
float f32() { |
|
|
|
// The multiplier is 2f7fffff in hex. This gives maximum precision of uint32_t -> float conversion and its image is [0, 1). |
|
|
|
return u32() * 2.32830629e-10f; |
|
|
|
} |
|
|
|
float f64() { |
|
|
|
return u64() * 5.421010862427522e-20; |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Simple random API |
|
|
|
// Simple thread-local API |
|
|
|
|
|
|
|
/** Initializes the thread-local RNG state. |
|
|
|
Must call when creating a thread, otherwise random state is undefined (might always return 0). |
|
|
@@ -92,26 +70,91 @@ void init(); |
|
|
|
|
|
|
|
/** Returns the thread-local generator. |
|
|
|
*/ |
|
|
|
Xoroshiro128Plus& get(); |
|
|
|
Xoroshiro128Plus& local(); |
|
|
|
|
|
|
|
/** Returns a uniform random uint64_t from 0 to UINT64_MAX */ |
|
|
|
inline uint64_t u64() { |
|
|
|
return get().u64(); |
|
|
|
template <typename T> |
|
|
|
T get() { |
|
|
|
// Call operator()() and cast by default |
|
|
|
return local()(); |
|
|
|
} |
|
|
|
/** Returns a uniform random uint32_t from 0 to UINT32_MAX */ |
|
|
|
inline uint32_t u32() { |
|
|
|
return get().u32(); |
|
|
|
|
|
|
|
template <> |
|
|
|
inline uint32_t get() { |
|
|
|
// Take top 32 bits which has better randomness properties. |
|
|
|
return get<uint64_t>() >> 32; |
|
|
|
} |
|
|
|
/** Returns a uniform random float in the interval [0.0, 1.0) */ |
|
|
|
inline float uniform() { |
|
|
|
return get().f32(); |
|
|
|
|
|
|
|
template <> |
|
|
|
inline uint16_t get() { |
|
|
|
return get<uint64_t>() >> 48; |
|
|
|
} |
|
|
|
|
|
|
|
template <> |
|
|
|
inline uint8_t get() { |
|
|
|
return get<uint64_t>() >> 56; |
|
|
|
} |
|
|
|
|
|
|
|
template <> |
|
|
|
inline bool get() { |
|
|
|
return get<uint64_t>() >> 63; |
|
|
|
} |
|
|
|
|
|
|
|
template <> |
|
|
|
inline float get() { |
|
|
|
// The multiplier is 2f7fffff in hex. This gives maximum precision of uint32_t -> float conversion and its image is [0, 1). |
|
|
|
return get<uint32_t>() * 2.32830629e-10f; |
|
|
|
} |
|
|
|
|
|
|
|
template <> |
|
|
|
inline double get() { |
|
|
|
return get<uint64_t>() * 5.421010862427522e-20; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/** Returns a uniform random uint64_t from 0 to UINT64_MAX */ |
|
|
|
inline uint64_t u64() {return get<uint64_t>();} |
|
|
|
/** Returns a uniform random uint32_t from 0 to UINT32_MAX */ |
|
|
|
inline uint32_t u32() {return get<uint32_t>();} |
|
|
|
/** Returns a uniform random float in the interval [0.0, 1.0) */ |
|
|
|
inline float uniform() {return get<float>();} |
|
|
|
|
|
|
|
/** Returns a normal random number with mean 0 and standard deviation 1 */ |
|
|
|
float normal(); |
|
|
|
inline float normal() { |
|
|
|
// Box-Muller transform |
|
|
|
float radius = std::sqrt(-2.f * std::log(1.f - get<float>())); |
|
|
|
float theta = 2.f * M_PI * get<float>(); |
|
|
|
return radius * std::sin(theta); |
|
|
|
|
|
|
|
// // Central Limit Theorem |
|
|
|
// const int n = 8; |
|
|
|
// float sum = 0.0; |
|
|
|
// for (int i = 0; i < n; i++) { |
|
|
|
// sum += get<float>(); |
|
|
|
// } |
|
|
|
// return (sum - n / 2.f) / std::sqrt(n / 12.f); |
|
|
|
} |
|
|
|
|
|
|
|
/** Fills an array with random bytes. */ |
|
|
|
void buffer(uint8_t* out, size_t len); |
|
|
|
inline void buffer(uint8_t* out, size_t len) { |
|
|
|
Xoroshiro128Plus& rng = local(); |
|
|
|
for (size_t i = 0; i < len; i += 4) { |
|
|
|
uint64_t r = rng(); |
|
|
|
out[i] = r; |
|
|
|
if (i + 1 < len) |
|
|
|
out[i + 1] = r >> 8; |
|
|
|
if (i + 2 < len) |
|
|
|
out[i + 2] = r >> 16; |
|
|
|
if (i + 3 < len) |
|
|
|
out[i + 3] = r >> 24; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** Creates a vector of random bytes. */ |
|
|
|
std::vector<uint8_t> vector(size_t len); |
|
|
|
inline std::vector<uint8_t> vector(size_t len) { |
|
|
|
std::vector<uint8_t> v(len); |
|
|
|
buffer(v.data(), len); |
|
|
|
return v; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
} // namespace random |
|
|
|