Advertisement
ulfben

Jenkins 32-bit Small Fast PRNG

Jun 11th, 2024
408
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 3.81 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++ 32-bit two-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 u32 = uint32_t;
  15.     u32 a;
  16.     u32 b;
  17.     u32 c;
  18.     u32 d;    
  19.  
  20.     static constexpr u32 rot(u32 x, u32 k) noexcept {
  21.         return (x << k) | (x >> (32 - k));
  22.     }
  23.  
  24. public:
  25.     constexpr SmallPRNG(u32 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 u32, 4> state) noexcept : a(state[0]), b(state[1]), c(state[2]), d(state[3]) {}
  32.  
  33.     constexpr u32 max() noexcept{
  34.         return std::numeric_limits<u32>::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 u32 next() noexcept {  
  66.         const u32 e = a - rot(b, 27);
  67.         a = b ^ rot(c, 17);
  68.         b = c + d;
  69.         c = d + e;
  70.         d = e + a;
  71.         return d;
  72.     }
  73.  
  74.     template<typename T = float>
  75.     constexpr T next_gaussian(T mean, T stddev) noexcept {
  76.         static_assert(std::is_floating_point_v<T>, "next_guassian can only be used with a floating point type");
  77.         static bool hasSpare = false;
  78.         static T spare;
  79.  
  80.         if (hasSpare) {
  81.             hasSpare = false;
  82.             return mean + stddev * spare;
  83.         }
  84.  
  85.         hasSpare = true;
  86.         T u, v, s;
  87.         do {
  88.             u = unit_range<T>();
  89.             v = unit_range<T>();
  90.             s = u * u + v * v;
  91.         } while (s >= T(1.0) || s == T(0.0));
  92.         s = std::sqrt(T(-2.0) * std::log(s) / s);
  93.         spare = v * s;
  94.         return mean + stddev * (u * s);
  95.     }
  96.  
  97.     constexpr std::array<u32, 4> get_state() const noexcept {
  98.         return {a, b, c, d};
  99.     }
  100.     constexpr void set_state(std::span<const u32, 4> s) noexcept {
  101.         *this = SmallPRNG(s);
  102.     }
  103. };
  104.  
  105. int main() {
  106.     SmallPRNG rand(223456721);
  107.  
  108.     [[maybe_unused]] auto random_unsigned = rand.between(10u, 50u);
  109.     [[maybe_unused]] int random_int = rand.between(-10, 10);
  110.     [[maybe_unused]] float random_normalized = rand.normalized(); //0.0 - 1.0
  111.     [[maybe_unused]] float ndc = rand.unit_range();  //-1.0 - +1.0
  112.     [[maybe_unused]] auto gaus = rand.next_gaussian(70.0f, 10.0f);
  113.     [[maybe_unused]] double random_double = rand.between(1.0, 5.0);
  114.  
  115.     auto state = rand.get_state();
  116.     rand.set_state(state);
  117.  
  118.     return random_unsigned;
  119. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement