You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

random.hpp 3.3KB

3 years ago
3 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. #pragma once
  2. #include <common.hpp>
  3. #include <random>
  4. #include <vector>
  5. namespace rack {
  6. /** Random number generation */
  7. namespace random {
  8. /** xoroshiro128+. Very fast, not-cryptographic random number generator.
  9. From https://prng.di.unimi.it/
  10. Example:
  11. std::random_device rd;
  12. random::Xoroshiro128Plus rng(rd(), rd());
  13. uint64_t r = rng();
  14. std::uniform_real_distribution<float> uniform(0.f, 1.f);
  15. float r = uniform(rng);
  16. std::normal_distribution<> normal(0.0, 1.0);
  17. double r = normal(rng);
  18. */
  19. struct Xoroshiro128Plus {
  20. uint64_t state[2] = {};
  21. void seed(uint64_t s0, uint64_t s1) {
  22. state[0] = s0;
  23. state[1] = s1;
  24. // A bad seed will give a bad first result, so shift the state
  25. operator()();
  26. }
  27. bool isSeeded() {
  28. return state[0] || state[1];
  29. }
  30. static uint64_t rotl(uint64_t x, int k) {
  31. return (x << k) | (x >> (64 - k));
  32. }
  33. uint64_t operator()() {
  34. uint64_t s0 = state[0];
  35. uint64_t s1 = state[1];
  36. uint64_t result = s0 + s1;
  37. s1 ^= s0;
  38. state[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14);
  39. state[1] = rotl(s1, 36);
  40. return result;
  41. }
  42. constexpr uint64_t min() const {
  43. return 0;
  44. }
  45. constexpr uint64_t max() const {
  46. return UINT64_MAX;
  47. }
  48. };
  49. // Simple global API
  50. void init();
  51. /** Returns the generator.
  52. Named "local" because the generator was thread-local in previous versions.
  53. */
  54. Xoroshiro128Plus& local();
  55. template <typename T>
  56. T get() {
  57. // Call operator()() and cast by default
  58. return local()();
  59. }
  60. template <>
  61. inline uint32_t get() {
  62. // Take top 32 bits which has better randomness properties.
  63. return get<uint64_t>() >> 32;
  64. }
  65. template <>
  66. inline uint16_t get() {
  67. return get<uint64_t>() >> 48;
  68. }
  69. template <>
  70. inline uint8_t get() {
  71. return get<uint64_t>() >> 56;
  72. }
  73. template <>
  74. inline bool get() {
  75. return get<uint64_t>() >> 63;
  76. }
  77. template <>
  78. inline float get() {
  79. // The multiplier is 2f7fffff in hex. This gives maximum precision of uint32_t -> float conversion and its image is [0, 1).
  80. return get<uint32_t>() * 2.32830629e-10f;
  81. }
  82. template <>
  83. inline double get() {
  84. return get<uint64_t>() * 5.421010862427522e-20;
  85. }
  86. /** Returns a uniform random uint64_t from 0 to UINT64_MAX */
  87. inline uint64_t u64() {return get<uint64_t>();}
  88. /** Returns a uniform random uint32_t from 0 to UINT32_MAX */
  89. inline uint32_t u32() {return get<uint32_t>();}
  90. /** Returns a uniform random float in the interval [0.0, 1.0) */
  91. inline float uniform() {return get<float>();}
  92. /** Returns a normal random number with mean 0 and standard deviation 1 */
  93. inline float normal() {
  94. // Box-Muller transform
  95. float radius = std::sqrt(-2.f * std::log(1.f - get<float>()));
  96. float theta = 2.f * M_PI * get<float>();
  97. return radius * std::sin(theta);
  98. // // Central Limit Theorem
  99. // const int n = 8;
  100. // float sum = 0.0;
  101. // for (int i = 0; i < n; i++) {
  102. // sum += get<float>();
  103. // }
  104. // return (sum - n / 2.f) / std::sqrt(n / 12.f);
  105. }
  106. /** Fills an array with random bytes. */
  107. inline void buffer(uint8_t* out, size_t len) {
  108. Xoroshiro128Plus& rng = local();
  109. for (size_t i = 0; i < len; i += 4) {
  110. uint64_t r = rng();
  111. out[i] = r;
  112. if (i + 1 < len)
  113. out[i + 1] = r >> 8;
  114. if (i + 2 < len)
  115. out[i + 2] = r >> 16;
  116. if (i + 3 < len)
  117. out[i + 3] = r >> 24;
  118. }
  119. }
  120. /** Creates a vector of random bytes. */
  121. inline std::vector<uint8_t> vector(size_t len) {
  122. std::vector<uint8_t> v(len);
  123. buffer(v.data(), len);
  124. return v;
  125. }
  126. } // namespace random
  127. } // namespace rack