Advertisement
ulfben

Jenkins Small Fast PRNG

Jun 8th, 2024 (edited)
332
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 4.01 KB | None | 0 0
  1. #include <array>
  2. #include <cstdint>
  3. #include <cassert>
  4. #include <limits>
  5. #include <span>
  6. #include <cmath>
  7. /*
  8. A C++ 64-bit three-rotate implementation of the famous Jenkins Small Fast PRNG.
  9. Original public domain C-code and writeup by Bob Jenkins https://burtleburtle.net/bob/rand/smallprng.html
  10. C++ implementation by Ulf Benjaminsson (ulfbenjaminsson.com), also placed in the public domain. Use freely!
  11. */
  12. class SmallPRNG
  13.  {
  14.     using u64 = uint64_t;
  15.     u64 a;
  16.     u64 b;
  17.     u64 c;
  18.     u64 d;    
  19.  
  20.     static constexpr u64 rot(u64 x, u64 k) noexcept {
  21.         return (x << k) | (x >> (64 - k));
  22.     }
  23.  
  24. public:
  25.     constexpr SmallPRNG(u64 seed) noexcept : a(0xf1ea5eed), b(seed), c(seed), d(seed) {        
  26.         // warmup: run the generator a couple of cycles to mix the state thoroughly
  27.         for (auto i = 0; i < 20; ++i) {
  28.             next();
  29.         }
  30.     }
  31.     constexpr SmallPRNG(std::span<const u64, 4> state) noexcept : a(state[0]), b(state[1]), c(state[2]), d(state[3]) {}
  32.  
  33.     constexpr u64 max() noexcept{
  34.         return std::numeric_limits<u64>::max();
  35.     }
  36.  
  37.     constexpr bool coinToss() noexcept{
  38.         return next() & 1; //checks the least significant bit
  39.     }
  40.  
  41.     template<typename T>
  42.     constexpr T between(T min, T max) noexcept {
  43.         assert(min < max && "SmallPRNG::between(min, max) called with inverted range.");
  44.         if constexpr (std::is_floating_point_v<T>) {
  45.             return min + (max - min) * normalized<T>();
  46.         } else if constexpr (std::is_integral_v<T>) {
  47.             using UT = std::make_unsigned_t<T>;
  48.             UT range = static_cast<UT>(max - min);
  49.             UT num = next() % (range + 1);
  50.             return min + static_cast<T>(num);
  51.         }
  52.     }
  53.  
  54.     template<typename T = float>
  55.     constexpr T normalized() noexcept {
  56.         return static_cast<T>(next()) / (static_cast<long double>(max()) + 1.0L);
  57.     }
  58.  
  59.     template<typename T = float>
  60.     constexpr T unit_range() noexcept {
  61.         static_assert(std::is_floating_point_v<T>, "raunit_range can only be used with floating point types.");
  62.         return static_cast<T>(2.0) * normalized<T>() - static_cast<T>(1.0);
  63.     }
  64.  
  65.     constexpr u64 next() noexcept {
  66.         // The rotate constants (7, 13, 37) are chosen specifically for 64-bit term, to provide
  67.         // better avalanche characteristics, achieving 18.4 bits of avalanche after 5 rounds.
  68.         const u64 e = a - rot(b, 7);
  69.         a = b ^ rot(c, 13);
  70.         b = c + rot(d, 37);
  71.         c = d + e;
  72.         d = e + a;
  73.         return d;
  74.     }
  75.  
  76.     template<typename T = float>
  77.     constexpr T next_gaussian(T mean, T stddev) noexcept {
  78.         static_assert(std::is_floating_point_v<T>, "next_guassian can only be used with a floating point type");
  79.         static bool hasSpare = false;
  80.         static T spare;
  81.  
  82.         if (hasSpare) {
  83.             hasSpare = false;
  84.             return mean + stddev * spare;
  85.         }
  86.  
  87.         hasSpare = true;
  88.         T u, v, s;
  89.         do {
  90.             u = unit_range<T>();
  91.             v = unit_range<T>();
  92.             s = u * u + v * v;
  93.         } while (s >= T(1.0) || s == T(0.0));
  94.         s = std::sqrt(T(-2.0) * std::log(s) / s);
  95.         spare = v * s;
  96.         return mean + stddev * (u * s);
  97.     }
  98.  
  99.     constexpr std::array<u64, 4> get_state() const noexcept {
  100.         return {a, b, c, d};
  101.     }
  102.     constexpr void set_state(std::span<const u64, 4> s) noexcept {
  103.         *this = SmallPRNG(s);
  104.     }
  105. };
  106.  
  107. int main() {
  108.     SmallPRNG rand(223456721);
  109.  
  110.     [[maybe_unused]] auto random_unsigned = rand.between(10u, 50u);
  111.     [[maybe_unused]] int random_int = rand.between(-10, 10);
  112.     [[maybe_unused]] float random_normalized = rand.normalized(); //0.0 - 1.0
  113.     [[maybe_unused]] float ndc = rand.unit_range();  //-1.0 - +1.0
  114.     //[[maybe_unused]] auto gaus = rand.next_gaussian(70.0f, 10.0f);
  115.     [[maybe_unused]] double random_double = rand.between(1.0, 5.0);
  116.  
  117.     auto state = rand.get_state();
  118.     rand.set_state(state);
  119.  
  120.     return random_unsigned;
  121. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement