diff options
author | André Nusser <andre.nusser@googlemail.com> | 2017-01-08 18:39:19 +0100 |
---|---|---|
committer | André Nusser <andre.nusser@googlemail.com> | 2017-01-08 19:12:59 +0100 |
commit | b9ae5c7b6863eab945438972c6b7c905a1924297 (patch) | |
tree | e2e817350963586cddfb578f52d97fe3d9f007f8 | |
parent | 8dc3c0e2db6ce4f571d5b763c396bdab49f94889 (diff) |
Replace used <random> methods by own.
This makes our random numbers cross-platform.
-rw-r--r-- | src/random.cc | 85 | ||||
-rw-r--r-- | src/random.h | 10 |
2 files changed, 86 insertions, 9 deletions
diff --git a/src/random.cc b/src/random.cc index 3d94a25..f6b5b34 100644 --- a/src/random.cc +++ b/src/random.cc @@ -27,6 +27,8 @@ #include "random.h" #include <chrono> +#include <type_traits> +#include <cmath> Random::Random() : Random(std::chrono::system_clock::now().time_since_epoch().count()) @@ -45,18 +47,89 @@ void Random::setSeed(unsigned int seed) int Random::intInRange(int lower_bound, int upper_bound) { - std::uniform_int_distribution<int> distribution(lower_bound, upper_bound); - return distribution(generator); + auto generate = [this]() + { + return (int)generator() - generator.min(); + }; + + const int in_range = generator.max() - generator.min(); + const int out_range = upper_bound - lower_bound; + + int rand; + + // scale in_range DOWN to out_range. + // (see: http://www.azillionmonkeys.com/qed/random.html) + if (in_range > out_range) + { + const int rand_inv_range = in_range / (out_range + 1); + + do + { + rand = generate(); + } + while (rand >= (out_range + 1) * rand_inv_range); + + rand = lower_bound + rand/rand_inv_range; + } + // scale in_range UP to out_range. + // (see: http://stackoverflow.com/a/30738381) + else if (in_range < out_range) + { + int scale = out_range / (in_range + 1); + + do + { + rand = generate() + intInRange(0, scale) * (in_range + 1); + } + while (rand < lower_bound && rand > upper_bound); + + rand = lower_bound + rand; + } + // naive case + else + { + rand = lower_bound + generate(); + } + + return rand; } float Random::floatInRange(float lower_bound, float upper_bound) { - std::uniform_real_distribution<float> distribution(lower_bound, upper_bound); - return distribution(generator); + return generateFloat() * (upper_bound - lower_bound) + lower_bound; } +// For details regarding the algorithm see: +// https://en.wikipedia.org/wiki/Marsaglia_polar_method float Random::normalDistribution(float mean, float stddev) { - std::normal_distribution<float> distribution(mean, stddev); - return distribution(generator); + if (has_saved_value) + { + has_saved_value = false; + return saved_value * stddev + mean; + } + else + { + float u, v, s; + do + { + u = 2.0f*generateFloat() - 1; + v = 2.0f*generateFloat() - 1; + s = (u * u) + (v * v); + } + while (s > 1.0f || s == 0.0f); + + s = std::sqrt(-2*std::log(s) / s); + saved_value = u * s; + has_saved_value = true; + + return mean + stddev * (v * s); + } +} + +float Random::generateFloat() +{ + return std::generate_canonical<float, + std::numeric_limits<float>::digits, + decltype(generator)>(generator); } diff --git a/src/random.h b/src/random.h index a18f5b6..7f53c05 100644 --- a/src/random.h +++ b/src/random.h @@ -56,12 +56,16 @@ private: // minstd_rand0, so make sure we use that all platforms regardless of which // stdlib we link with. std::minstd_rand0 generator; + + float generateFloat(); + + // variables for normalDistribution + bool has_saved_value = false; + float saved_value; }; template <typename T> T& Random::choose(std::vector<T>& vec) { - std::uniform_int_distribution<size_t> distribution(0, vec.size()-1); - size_t rand_index = distribution(generator); - return vec[rand_index]; + return vec[intInRange(0, vec.size() - 1)]; } |