|
1 |
| -#ifndef _FAST_RNG_H__ |
2 |
| -#define _FAST_RNG_H__ |
3 |
| - |
4 |
| -#include <cmath> |
5 |
| -#include <limits.h> |
6 |
| - |
7 |
| -/** A fast more effective seeded random number generator struct, made by kiss rng. |
| 1 | +/**************************************************************************** |
| 2 | + Copyright (c) 2012 cocos2d-x.org |
| 3 | + Copyright (c) 2013-2016 Chukong Technologies Inc. |
| 4 | + Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd. |
| 5 | + Copyright (c) 2019-present Axmol Engine contributors (see AUTHORS.md). |
| 6 | +
|
| 7 | + https://axmol.dev/ |
| 8 | +
|
| 9 | + Permission is hereby granted, free of charge, to any person obtaining a copy |
| 10 | + of this software and associated documentation files (the "Software"), to deal |
| 11 | + in the Software without restriction, including without limitation the rights |
| 12 | + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| 13 | + copies of the Software, and to permit persons to whom the Software is |
| 14 | + furnished to do so, subject to the following conditions: |
| 15 | +
|
| 16 | + The above copyright notice and this permission notice shall be included in |
| 17 | + all copies or substantial portions of the Software. |
| 18 | +
|
| 19 | + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| 20 | + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| 21 | + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| 22 | + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| 23 | + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| 24 | + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| 25 | + THE SOFTWARE. |
| 26 | + ****************************************************************************/ |
| 27 | + |
| 28 | +#ifndef __FAST_RNG_H__ |
| 29 | +#define __FAST_RNG_H__ |
| 30 | + |
| 31 | +/** A fast more effective seeded random number generator struct, uses xoshiro128**. |
8 | 32 | * It uses a simple algorithm to improve the speed of generating random numbers with a decent quality,
|
9 | 33 | * Use this if you're planning to generate large amounts of random numbers in a single frame.
|
10 | 34 | *
|
11 | 35 | * @since axmol-1.0.0b8
|
12 | 36 | */
|
13 | 37 | struct FastRNG
|
14 | 38 | {
|
15 |
| - const uint32_t RNG_RAND_MAX = std::numeric_limits<uint32_t>::max(); |
16 |
| - uint32_t _x = 1; |
17 |
| - uint32_t _y = 2; |
18 |
| - uint32_t _z = 4; |
19 |
| - uint32_t _w = 8; |
20 |
| - uint32_t _carry = 0; |
21 |
| - uint32_t _k = 0; |
22 |
| - uint32_t _m = 0; |
23 |
| - uint64_t _seed = 0; |
24 |
| - |
25 |
| - FastRNG() { seed_rng((uint32_t)time(NULL)); } |
26 |
| - FastRNG(uint64_t seed) { seed_rng_64(seed); } |
27 |
| - |
28 |
| - // initialize this object with seed |
29 |
| - void seed_rng(uint64_t seed) |
| 39 | + uint32_t s[4]; |
| 40 | + |
| 41 | + // SplitMix64 implementation, doesn't modify any state for this instance |
| 42 | + // but it is used to seed xoshiro128** state |
| 43 | + uint64_t nextSeed(uint64_t& state) |
30 | 44 | {
|
31 |
| - auto seed1 = static_cast<uint32_t>(seed); |
32 |
| - auto seed2 = static_cast<uint32_t>(seed >> 32); |
33 |
| - |
34 |
| - _seed = seed; |
35 |
| - _x = static_cast<int32_t>(seed1) | 1; |
36 |
| - _y = static_cast<int32_t>(seed2) | 2; |
37 |
| - _z = static_cast<int32_t>(seed1) | 4; |
38 |
| - _w = static_cast<int32_t>(seed2) | 8; |
39 |
| - _carry = 0; |
| 45 | + uint64_t z = (state += 0x9e3779b97f4a7c15); |
| 46 | + z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9; |
| 47 | + z = (z ^ (z >> 27)) * 0x94d049bb133111eb; |
| 48 | + return z ^ (z >> 31); |
40 | 49 | }
|
41 | 50 |
|
42 |
| - // initialize this object with a uint64_t seed |
43 |
| - // DEPRECATED: use seed_rng instead |
44 |
| - void seed_rng_64(uint64_t seed) { seed_rng(seed); } |
| 51 | + FastRNG() { seed(static_cast<uint64_t>(rand()) << 32 | rand()); } |
45 | 52 |
|
46 |
| - // returns a random uint32_t value |
47 |
| - uint32_t rng() |
| 53 | + // there is no need to seed this instance of FastRNG |
| 54 | + // because it has already been seeded with rand() by constructor |
| 55 | + // you can override the seed by giving your own 64-bit seed |
| 56 | + void seed(uint64_t seed) |
48 | 57 | {
|
49 |
| - _x = _x * 69069 + 12345; |
50 |
| - _y ^= _y << 13; |
51 |
| - _y ^= _y >> 17; |
52 |
| - _y ^= _y << 5; |
53 |
| - _k = (_z >> 2) + (_w >> 3) + (_carry >> 2); |
54 |
| - _m = _w + _w + _z + _carry; |
55 |
| - _z = _w; |
56 |
| - _w = _m; |
57 |
| - _carry = _k >> 30; |
58 |
| - return _x + _y + _w; |
| 58 | + uint64_t state = seed; |
| 59 | + uint64_t states[2]; |
| 60 | + memset(states, 0, 16); |
| 61 | + states[0] = nextSeed(state); |
| 62 | + states[1] = nextSeed(state); |
| 63 | + memcpy(s, states, 16); |
59 | 64 | }
|
60 | 65 |
|
61 |
| - // returns a random integer from min to max |
62 |
| - int32_t range(int32_t min, int32_t max) |
| 66 | + // returns a copy of x rotated k bits to the left |
| 67 | + static inline uint32_t rotL(const uint32_t x, int k) { return (x << k) | (x >> (32 - k)); } |
| 68 | + |
| 69 | + // steps once into the state, returns a random from 0 to UINT32_MAX |
| 70 | + uint32_t next() |
63 | 71 | {
|
64 |
| - return static_cast<int32_t>(min + rng() / (RNG_RAND_MAX / (max - min))); |
| 72 | + const uint32_t result = rotL(s[1] * 5, 7) * 9; |
| 73 | + |
| 74 | + const uint32_t t = s[1] << 9; |
| 75 | + |
| 76 | + s[2] ^= s[0]; |
| 77 | + s[3] ^= s[1]; |
| 78 | + s[1] ^= s[2]; |
| 79 | + s[0] ^= s[3]; |
| 80 | + |
| 81 | + s[2] ^= t; |
| 82 | + |
| 83 | + s[3] = rotL(s[3], 11); |
| 84 | + |
| 85 | + return result; |
65 | 86 | }
|
66 | 87 |
|
67 |
| - // returns a random unsigned integer from min to max |
68 |
| - uint32_t rangeu(uint32_t min, uint32_t max) |
| 88 | + // generates a random real that ranges from 0.0 to 1.0 |
| 89 | + template <typename T> |
| 90 | + T nextReal() |
69 | 91 | {
|
70 |
| - return static_cast<uint32_t>(min + rng() / (RNG_RAND_MAX / (max - min))); |
| 92 | + if (std::is_same<T, float>::value) |
| 93 | + return static_cast<T>(next() >> 8) * 0x1.0p-24f; |
| 94 | + else if (std::is_same<T, double>::value) |
| 95 | + return static_cast<T>((static_cast<uint64_t>(next()) << 32 | next()) >> 11) * 0x1.0p-53; |
| 96 | + return 0; // possibly assert? |
71 | 97 | }
|
72 | 98 |
|
73 |
| - // returns a random integer from 0 to max |
74 |
| - int32_t max(int32_t max = INT_MAX) |
| 99 | + // generates a random real that ranges from min to max |
| 100 | + template <typename T> |
| 101 | + T nextReal(T min, T max) |
75 | 102 | {
|
76 |
| - return static_cast<int32_t>(static_cast<float>(rng()) / static_cast<float>(RNG_RAND_MAX / (max - 0))); |
| 103 | + return static_cast<T>(min + nextReal<T>() * (max - min)); |
77 | 104 | }
|
78 | 105 |
|
79 |
| - // returns a random unsigned integer from 0 to max |
80 |
| - uint32_t maxu(uint32_t max = UINT_MAX) { return static_cast<uint32_t>(0 + rng() / (RNG_RAND_MAX / (max - 0))); } |
81 |
| - |
82 |
| - // returns a random float from min to max |
83 |
| - float rangef(float min = -1.0F, float max = 1.0F) |
| 106 | + // generates a random integer that ranges from min inclusive to max exclusive [min, max) and is uniformly distributed using fastrange algorithm |
| 107 | + template <typename T> |
| 108 | + T nextInt(T min, T max) |
84 | 109 | {
|
85 |
| - return min + static_cast<float>(rng()) / (static_cast<float>(RNG_RAND_MAX) / (max - min)); |
| 110 | + return min + static_cast<T>(nextMax(static_cast<uint32_t>(max - min))); |
86 | 111 | }
|
87 | 112 |
|
88 |
| - // returns a random float from 0.0 to max |
89 |
| - float maxf(float max) { return static_cast<float>(rng()) / (static_cast<float>(RNG_RAND_MAX) / (max - 0.0f)); } |
90 |
| - |
91 |
| - // returns a random float from 0.0 to 1.0 |
92 |
| - float float01() { return static_cast<float>(rng()) / (static_cast<float>(RNG_RAND_MAX) / (1.0f - 0.0f)); } |
| 113 | + // generates a random integer from 0 to max exclusive that is uniformly distributed using fastrange algorithm |
| 114 | + uint32_t nextMax(uint32_t max) |
| 115 | + { |
| 116 | + uint64_t multiresult = static_cast<uint64_t>(next()) * max; |
| 117 | + uint32_t leftover = static_cast<uint32_t>(multiresult); |
| 118 | + if (leftover < max) |
| 119 | + { |
| 120 | + uint32_t threshold = (0 - max) % max; |
| 121 | + while (leftover < threshold) |
| 122 | + { |
| 123 | + multiresult = static_cast<uint64_t>(next()) * max; |
| 124 | + leftover = static_cast<uint32_t>(multiresult); |
| 125 | + } |
| 126 | + } |
| 127 | + return multiresult >> 32; |
| 128 | + } |
93 | 129 |
|
94 |
| - // returns either false or true randomly |
95 |
| - bool bool01() { return static_cast<bool>(rng() & 1); } |
| 130 | + // wrapper for nextInt<int32_t>(min, max) |
| 131 | + int32_t range(int32_t min, int32_t max) { return nextInt<int32_t>(min, max); } |
| 132 | + // wrapper for nextInt<int32_t>(0, max) |
| 133 | + int32_t max(int32_t max = INT32_MAX) { return nextInt<int32_t>(0, max); } |
| 134 | + |
| 135 | + // wrapper for nextInt<uint32_t>(min, max) |
| 136 | + uint32_t rangeu(uint32_t min, uint32_t max) { return nextInt<uint32_t>(min, max); } |
| 137 | + // wrapper for nextInt<uint32_t>(0, max) |
| 138 | + uint32_t maxu(uint32_t max = UINT_MAX) { return nextInt<uint32_t>(0, max); } |
| 139 | + |
| 140 | + // wrapper for nextReal<float>(min, max) |
| 141 | + float rangef(float min = -1.0F, float max = 1.0F) { return nextReal<float>(min, max); } |
| 142 | + // wrapper for nextReal<float>(0, max) |
| 143 | + float maxf(float max) { return nextReal<float>(0, max); } |
| 144 | + |
| 145 | + // wrapper for nextReal<double>(min, max) |
| 146 | + double ranged(double min = -1.0F, double max = 1.0F) { return nextReal<double>(min, max); } |
| 147 | + // wrapper for nextReal<double>(0, max) |
| 148 | + double maxd(double max) { return nextReal<double>(0, max); } |
| 149 | + |
| 150 | + // wrapper for nextReal<float>() |
| 151 | + float float01() { return nextReal<float>(); } |
| 152 | + // wrapper for nextReal<double>() |
| 153 | + float double01() { return nextReal<double>(); } |
| 154 | + // wrapper for next() & 1, true or false based on LSB |
| 155 | + float bool01() { return next() & 1; } |
96 | 156 | };
|
97 | 157 |
|
98 |
| -#endif // _FAST_RNG_H__ |
| 158 | +#endif // __FAST_RNG_H__ |
0 commit comments