Advertisement
ulfben

randutils.hpp

Feb 8th, 2025
60
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 26.92 KB | None | 0 0
  1. /*
  2.  * Random-Number Utilities (randutil)
  3.  *     Addresses common issues with C++11 random number generation.
  4.  *     Makes good seeding easier, and makes using RNGs easy while retaining
  5.  *     all the power.
  6.  *
  7.  * The MIT License (MIT)
  8.  *
  9.  * Copyright (c) 2015-2022 Melissa E. O'Neill
  10.  *
  11.  * Permission is hereby granted, free of charge, to any person obtaining a copy
  12.  * of this software and associated documentation files (the "Software"), to deal
  13.  * in the Software without restriction, including without limitation the rights
  14.  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  15.  * copies of the Software, and to permit persons to whom the Software is
  16.  * furnished to do so, subject to the following conditions:
  17.  *
  18.  * The above copyright notice and this permission notice shall be included in
  19.  * all copies or substantial portions of the Software.
  20.  *
  21.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  22.  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  23.  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  24.  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  25.  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  26.  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  27.  * SOFTWARE.
  28.  */
  29.  
  30. #ifndef RANDUTILS_HPP
  31. #define RANDUTILS_HPP 1
  32.  
  33. /*
  34.  * This header includes three class templates that can help make C++11
  35.  * random number generation easier to use.
  36.  *
  37.  * randutils::seed_seq_fe
  38.  *
  39.  *   Fixed-Entropy Seed sequence
  40.  *
  41.  *   Provides a replacement for std::seed_seq that avoids problems with bias,
  42.  *   performs better in empirical statistical tests, and executes faster in
  43.  *   normal-sized use cases.
  44.  *
  45.  *   In normal use, it's accessed via one of the following type aliases
  46.  *
  47.  *       randutils::seed_seq_fe128
  48.  *       randutils::seed_seq_fe256
  49.  *
  50.  *   It's discussed in detail at
  51.  *       http://www.pcg-random.org/posts/developing-a-seed_seq-alternative.html
  52.  *   and the motivation for its creation (what's wrong with std::seed_seq) here
  53.  *       http://www.pcg-random.org/posts/cpp-seeding-surprises.html
  54.  *
  55.  *
  56.  * randutils::auto_seeded
  57.  *
  58.  *   Extends a seed sequence class with a nondeterministic default constructor.
  59.  *   Uses a variety of local sources of entropy to portably initialize any
  60.  *   seed sequence to a good default state.
  61.  *
  62.  *   In normal use, it's accessed via one of the following type aliases, which
  63.  *   use seed_seq_fe128 and seed_seq_fe256 above.
  64.  *
  65.  *       randutils::auto_seed_128
  66.  *       randutils::auto_seed_256
  67.  *
  68.  *   It's discussed in detail at
  69.  *       http://www.pcg-random.org/posts/simple-portable-cpp-seed-entropy.html
  70.  *   and its motivation (why you can't just use std::random_device) here
  71.  *       http://www.pcg-random.org/posts/cpps-random_device.html
  72.  *
  73.  *
  74.  * randutils::random_generator
  75.  *
  76.  *   An Easy-to-Use Random API
  77.  *
  78.  *   Provides all the power of C++11's random number facility in an easy-to
  79.  *   use wrapper.
  80.  *
  81.  *   In normal use, it's accessed via one of the following type aliases, which
  82.  *   also use auto_seed_256 by default
  83.  *
  84.  *       randutils::default_rng
  85.  *       randutils::mt19937_rng
  86.  *
  87.  *   It's discussed in detail at
  88.  *       http://www.pcg-random.org/posts/ease-of-use-without-loss-of-power.html
  89.  */
  90.  
  91. #include <cstddef>
  92. #include <cstdint>
  93. #include <cstdlib>
  94. #include <random>
  95. #include <array>
  96. #include <functional>  // for std::hash
  97. #include <initializer_list>
  98. #include <utility>
  99. #include <type_traits>
  100. #include <iterator>
  101. #include <chrono>
  102. #include <thread>
  103. #include <algorithm>
  104.  
  105. // Ugly platform-specific code for auto_seeded
  106.  
  107. #if !defined(RANDUTILS_CPU_ENTROPY) && defined(__has_builtin)
  108.     #if __has_builtin(__builtin_readcyclecounter) && !defined(__aarch64__)
  109.         #define RANDUTILS_CPU_ENTROPY __builtin_readcyclecounter()
  110.     #endif
  111. #endif
  112. #if !defined(RANDUTILS_CPU_ENTROPY)
  113.     #if __i386__
  114.         #if __GNUC__
  115.             #define RANDUTILS_CPU_ENTROPY __builtin_ia32_rdtsc()
  116.         #else
  117.             #include <immintrin.h>
  118.             #define RANDUTILS_CPU_ENTROPY __rdtsc()
  119.         #endif
  120.     #else
  121.         #define RANDUTILS_CPU_ENTROPY 0
  122.     #endif
  123. #endif
  124.  
  125. #if defined(RANDUTILS_GETPID)
  126.     // Already defined externally
  127. #elif defined(_WIN64) || defined(_WIN32)
  128.     #include <process.h>
  129.     #define RANDUTILS_GETPID _getpid()
  130. #elif defined(__unix__) || defined(__unix) \
  131.       || (defined(__APPLE__) && defined(__MACH__))
  132.     #include <unistd.h>
  133.     #define RANDUTILS_GETPID getpid()
  134. #else
  135.     #define RANDUTILS_GETPID 0
  136. #endif
  137.  
  138. #if __cpp_constexpr >= 201304L
  139.     #define RANDUTILS_GENERALIZED_CONSTEXPR constexpr
  140. #else
  141.     #define RANDUTILS_GENERALIZED_CONSTEXPR
  142. #endif
  143.  
  144.  
  145.  
  146. namespace randutils {
  147.  
  148. //////////////////////////////////////////////////////////////////////////////
  149. //
  150. // seed_seq_fe
  151. //
  152. //////////////////////////////////////////////////////////////////////////////
  153.  
  154. /*
  155.  * seed_seq_fe implements a fixed-entropy seed sequence; it conforms to all
  156.  * the requirements of a Seed Sequence concept.
  157.  *
  158.  * seed_seq_fe<N> implements a seed sequence which seeds based on a store of
  159.  * N * 32 bits of entropy.  Typically, it would be initialized with N or more
  160.  * integers.
  161.  *
  162.  * seed_seq_fe128 and seed_seq_fe256 are provided as convenience typedefs for
  163.  * 128- and 256-bit entropy stores respectively.  These variants outperform
  164.  * std::seed_seq, while being better mixing the bits it is provided as entropy.
  165.  * In almost all common use cases, they serve as better drop-in replacements
  166.  * for seed_seq.
  167.  *
  168.  * Technical details
  169.  *
  170.  * Assuming it constructed with M seed integers as input, it exhibits the
  171.  * following properties
  172.  *
  173.  * * Diffusion/Avalanche:  A single-bit change in any of the M inputs has a
  174.  *   50% chance of flipping every bit in the bitstream produced by generate.
  175.  *   Initializing the N-word entropy store with M words requires O(N * M)
  176.  *   time precisely because of the avalanche requirements.  Once constructed,
  177.  *   calls to generate are linear in the number of words generated.
  178.  *
  179.  * * Bias freedom/Bijection: If M == N, the state of the entropy store is a
  180.  *   bijection from the M inputs (i.e., no states occur twice, none are
  181.  *   omitted). If M > N the number of times each state can occur is the same
  182.  *   (each state occurs 2**(32*(M-N)) times, where ** is the power function).
  183.  *   If M < N, some states cannot occur (bias) but no state occurs more
  184.  *   than once (it's impossible to avoid bias if M < N; ideally N should not
  185.  *   be chosen so that it is more than M).
  186.  *
  187.  *   Likewise, the generate function has similar properties (with the entropy
  188.  *   store as the input data).  If more outputs are requested than there is
  189.  *   entropy, some outputs cannot occur.  For example, the Mersenne Twister
  190.  *   will request 624 outputs, to initialize it's 19937-bit state, which is
  191.  *   much larger than a 128-bit or 256-bit entropy pool.  But in practice,
  192.  *   limiting the Mersenne Twister to 2**128 possible initializations gives
  193.  *   us enough initializations to give a unique initialization to trillions
  194.  *   of computers for billions of years.  If you really have 624 words of
  195.  *   *real* high-quality entropy you want to use, you probably don't need
  196.  *   an entropy mixer like this class at all.  But if you *really* want to,
  197.  *   nothing is stopping you from creating a randutils::seed_seq_fe<624>.
  198.  *
  199.  * * As a consequence of the above properties, if all parts of the provided
  200.  *   seed data are kept constant except one, and the remaining part is varied
  201.  *   through K different states, K different output sequences will be produced.
  202.  *
  203.  * * Also, because the amount of entropy stored is fixed, this class never
  204.  *   performs dynamic allocation and is free of the possibility of generating
  205.  *   an exception.
  206.  *
  207.  * Ideas used to implement this code include hashing, a simple PCG generator
  208.  * based on an MCG base with an XorShift output function and permutation
  209.  * functions on tuples.
  210.  *
  211.  * More detail at
  212.  *     http://www.pcg-random.org/posts/developing-a-seed_seq-alternative.html
  213.  */
  214.  
  215. template <size_t count = 4, typename IntRep = uint32_t,
  216.           size_t mix_rounds = 1 + (count <= 2)>
  217. struct seed_seq_fe {
  218. public:
  219.     // types
  220.     typedef IntRep result_type;
  221.  
  222. private:
  223.     static constexpr uint32_t INIT_A = 0x43b0d7e5;
  224.     static constexpr uint32_t MULT_A = 0x931e8875;
  225.  
  226.     static constexpr uint32_t INIT_B = 0x8b51f9dd;
  227.     static constexpr uint32_t MULT_B = 0x58f38ded;
  228.  
  229.     static constexpr uint32_t MIX_MULT_L = 0xca01f9dd;
  230.     static constexpr uint32_t MIX_MULT_R = 0x4973f715;
  231.     static constexpr uint32_t XSHIFT = sizeof(IntRep)*8/2;
  232.  
  233.     RANDUTILS_GENERALIZED_CONSTEXPR
  234.     static IntRep fast_exp(IntRep x, IntRep power)
  235.     {
  236.         IntRep result = IntRep(1);
  237.         IntRep multiplier = x;
  238.         while (power != IntRep(0)) {
  239.             IntRep thismult = power & IntRep(1) ? multiplier : IntRep(1);
  240.             result *= thismult;
  241.             power >>= 1;
  242.             multiplier *= multiplier;
  243.         }
  244.         return result;
  245.     }
  246.  
  247.     std::array<IntRep, count> mixer_;
  248.  
  249.     template <typename InputIter>
  250.     void mix_entropy(InputIter begin, InputIter end);
  251.  
  252. public:
  253.     seed_seq_fe(const seed_seq_fe&)     = delete;
  254.     void operator=(const seed_seq_fe&)  = delete;
  255.  
  256.     template <typename T>
  257.     seed_seq_fe(std::initializer_list<T> init)
  258.     {
  259.         seed(init.begin(), init.end());
  260.     }
  261.  
  262.     template <typename InputIter>
  263.     seed_seq_fe(InputIter begin, InputIter end)
  264.     {
  265.         seed(begin, end);
  266.     }
  267.  
  268.     // generating functions
  269.     template <typename RandomAccessIterator>
  270.     void generate(RandomAccessIterator first, RandomAccessIterator last) const;
  271.  
  272.     static constexpr size_t size()
  273.     {
  274.         return count;
  275.     }
  276.  
  277.     template <typename OutputIterator>
  278.     void param(OutputIterator dest) const;
  279.  
  280.     template <typename InputIter>
  281.     void seed(InputIter begin, InputIter end)
  282.     {
  283.         mix_entropy(begin, end);
  284.         // For very small sizes, we do some additional mixing.  For normal
  285.         // sizes, this loop never performs any iterations.
  286.         for (size_t i = 1; i < mix_rounds; ++i)
  287.             stir();
  288.     }
  289.  
  290.     seed_seq_fe& stir()
  291.     {
  292.         mix_entropy(mixer_.begin(), mixer_.end());
  293.         return *this;
  294.     }
  295.  
  296. };
  297.  
  298. template <size_t count, typename IntRep, size_t r>
  299. template <typename InputIter>
  300. void seed_seq_fe<count, IntRep, r>::mix_entropy(InputIter begin, InputIter end)
  301. {
  302.     auto hash_const = INIT_A;
  303.     auto hash = [&](IntRep value) {
  304.         value ^= hash_const;
  305.         hash_const *= MULT_A;
  306.         value *= hash_const;
  307.         value ^= value >> XSHIFT;
  308.         return value;
  309.     };
  310.     auto mix = [](IntRep x, IntRep y) {
  311.         IntRep result = MIX_MULT_L*x - MIX_MULT_R*y;
  312.         result ^= result >> XSHIFT;
  313.         return result;
  314.     };
  315.  
  316.     InputIter current = begin;
  317.     for (auto& elem : mixer_) {
  318.         if (current != end)
  319.             elem = hash(*current++);
  320.         else
  321.             elem = hash(0U);
  322.     }
  323.     for (auto& src : mixer_)
  324.         for (auto& dest : mixer_)
  325.             if (&src != &dest)
  326.                 dest = mix(dest,hash(src));
  327.     for (; current != end; ++current)
  328.         for (auto& dest : mixer_)
  329.             dest = mix(dest,hash(*current));
  330. }
  331.  
  332. template <size_t count, typename IntRep, size_t mix_rounds>
  333. template <typename OutputIterator>
  334. void seed_seq_fe<count,IntRep,mix_rounds>::param(OutputIterator dest) const
  335. {
  336.     const IntRep INV_A = fast_exp(MULT_A, IntRep(-1));
  337.     const IntRep MIX_INV_L = fast_exp(MIX_MULT_L, IntRep(-1));
  338.  
  339.     auto mixer_copy = mixer_;
  340.     for (size_t round = 0; round < mix_rounds; ++round) {
  341.         // Advance to the final value.  We'll backtrack from that.
  342.         auto hash_const = INIT_A*fast_exp(MULT_A, IntRep(count * count));
  343.  
  344.         for (auto src = mixer_copy.rbegin(); src != mixer_copy.rend(); ++src)
  345.             for (auto dest = mixer_copy.rbegin(); dest != mixer_copy.rend();
  346.                  ++dest)
  347.                 if (src != dest) {
  348.                     IntRep revhashed = *src;
  349.                     auto mult_const = hash_const;
  350.                     hash_const *= INV_A;
  351.                     revhashed ^= hash_const;
  352.                     revhashed *= mult_const;
  353.                     revhashed ^= revhashed >> XSHIFT;
  354.                     IntRep unmixed = *dest;
  355.                     unmixed ^= unmixed >> XSHIFT;
  356.                     unmixed += MIX_MULT_R*revhashed;
  357.                     unmixed *= MIX_INV_L;
  358.                     *dest = unmixed;
  359.                 }
  360.         for (auto i = mixer_copy.rbegin(); i != mixer_copy.rend(); ++i) {
  361.             IntRep unhashed = *i;
  362.             unhashed ^= unhashed >> XSHIFT;
  363.             unhashed *= fast_exp(hash_const, IntRep(-1));
  364.             hash_const *= INV_A;
  365.             unhashed ^= hash_const;
  366.             *i = unhashed;
  367.         }
  368.     }
  369.     std::copy(mixer_copy.begin(), mixer_copy.end(), dest);
  370. }
  371.  
  372.  
  373. template <size_t count, typename IntRep, size_t mix_rounds>
  374. template <typename RandomAccessIterator>
  375. void seed_seq_fe<count,IntRep,mix_rounds>::generate(
  376.         RandomAccessIterator dest_begin,
  377.         RandomAccessIterator dest_end) const
  378. {
  379.     auto src_begin = mixer_.begin();
  380.     auto src_end   = mixer_.end();
  381.     auto src       = src_begin;
  382.     auto hash_const = INIT_B;
  383.     for (auto dest = dest_begin; dest != dest_end; ++dest) {
  384.         auto dataval = *src;
  385.         if (++src == src_end)
  386.             src = src_begin;
  387.         dataval ^= hash_const;
  388.         hash_const *= MULT_B;
  389.         dataval *= hash_const;
  390.         dataval ^= dataval >> XSHIFT;
  391.         *dest = dataval;
  392.     }
  393. }
  394.  
  395. using seed_seq_fe128 = seed_seq_fe<4, uint32_t>;
  396. using seed_seq_fe256 = seed_seq_fe<8, uint32_t>;
  397.  
  398.  
  399. //////////////////////////////////////////////////////////////////////////////
  400. //
  401. // auto_seeded
  402. //
  403. //////////////////////////////////////////////////////////////////////////////
  404.  
  405. /*
  406.  * randutils::auto_seeded
  407.  *
  408.  *   Extends a seed sequence class with a nondeterministic default constructor.
  409.  *   Uses a variety of local sources of entropy to portably initialize any
  410.  *   seed sequence to a good default state.
  411.  *
  412.  *   In normal use, it's accessed via one of the following type aliases, which
  413.  *   use seed_seq_fe128 and seed_seq_fe256 above.
  414.  *
  415.  *       randutils::auto_seed_128
  416.  *       randutils::auto_seed_256
  417.  *
  418.  *   It's discussed in detail at
  419.  *       http://www.pcg-random.org/posts/simple-portable-cpp-seed-entropy.html
  420.  *   and its motivation (why you can't just use std::random_device) here
  421.  *       http://www.pcg-random.org/posts/cpps-random_device.html
  422.  */
  423.  
  424. template <typename SeedSeq>
  425. class auto_seeded : public SeedSeq {
  426.     using default_seeds = std::array<uint32_t, 13>;
  427.  
  428.     template <typename T>
  429.     static uint32_t crushto32(T value)
  430.     {
  431.         if (sizeof(T) <= 4)
  432.             return uint32_t(value);
  433.         else {
  434.             uint64_t result = uint64_t(value);
  435.             result *= 0xbc2ad017d719504d;
  436.             return uint32_t(result ^ (result >> 32));
  437.         }
  438.     }
  439.  
  440.     template <typename T>
  441.     static uint32_t hash(T&& value)
  442.     {
  443.         return crushto32(std::hash<typename std::remove_reference<
  444.                                     typename std::remove_cv<T>::type>::type>{}(
  445.                                      std::forward<T>(value)));
  446.     }
  447.  
  448.     static constexpr uint32_t fnv(uint32_t hash, const char* pos)
  449.     {
  450.         return *pos == '\0' ? hash : fnv((hash * 16777619U) ^ *pos, pos+1);
  451.     }
  452.  
  453.     default_seeds local_entropy()
  454.     {
  455.         // This is a constant that changes every time we compile the code
  456.         constexpr uint32_t compile_stamp =
  457.             fnv(2166136261U, __DATE__ __TIME__ __FILE__);
  458.  
  459.         // Some people think you shouldn't use the random device much because
  460.         // on some platforms it could be expensive to call or "use up" vital
  461.         // system-wide entropy, so we just call it once.
  462.         static uint32_t random_int = std::random_device{}();
  463.  
  464.         // The heap can vary from run to run as well.
  465.         void* malloc_addr = malloc(sizeof(int));
  466.         free(malloc_addr);
  467.         auto heap  = hash(malloc_addr);
  468.         auto stack = hash(&malloc_addr);
  469.  
  470.         // Every call, we increment our random int.  We don't care about race
  471.         // conditons.  The more, the merrier.
  472.         random_int += 0xedf19156;
  473.  
  474.         // Classic seed, the time.  It ought to change, especially since
  475.         // this is (hopefully) nanosecond resolution time.
  476.         auto hitime = std::chrono::high_resolution_clock::now()
  477.                         .time_since_epoch().count();
  478.  
  479.         // Address of the thing being initialized.  That can mean that
  480.         // different seed sequences in different places in memory will be
  481.         // different.  Even for the same object, it may vary from run to
  482.         // run in systems with ASLR, such as OS X, but on Linux it might not
  483.         // unless we compile with -fPIC -pic.
  484.         auto self_data = hash(this);
  485.  
  486.         // The address of the time function.  It should hopefully be in
  487.         // a system library that hopefully isn't always in the same place
  488.         // (might not change until system is rebooted though)
  489.         auto time_func = hash(&std::chrono::high_resolution_clock::now);
  490.  
  491.         // The address of the exit function.  It should hopefully be in
  492.         // a system library that hopefully isn't always in the same place
  493.         // (might not change until system is rebooted though).  Hopefully
  494.         // it's in a different library from time_func.
  495.         auto exit_func = hash(&_Exit);
  496.  
  497.         // The address of a local function.  That may be in a totally
  498.         // different part of memory.  On OS X it'll vary from run to run thanks
  499.         // to ASLR, on Linux it might not unless we compile with -fPIC -pic.
  500.         // Need the cast because it's an overloaded
  501.         // function and we need to pick the right one.
  502.         auto self_func = hash(
  503.             static_cast<uint32_t (*)(uint64_t)>(
  504.                                 &auto_seeded::crushto32));
  505.  
  506.         // Hash our thread id.  It seems to vary from run to run on OS X, not
  507.         // so much on Linux.
  508.         auto thread_id  = hash(std::this_thread::get_id());
  509.  
  510.         // Hash of the ID of a type.  May or may not vary, depending on
  511.         // implementation.
  512.         #if __cpp_rtti || __GXX_RTTI
  513.         auto type_id   = crushto32(typeid(*this).hash_code());
  514.         #else
  515.         uint32_t type_id   = 0;
  516.         #endif
  517.  
  518.         // Platform-specific entropy
  519.         auto pid = crushto32(RANDUTILS_GETPID);
  520.         auto cpu = crushto32(RANDUTILS_CPU_ENTROPY);
  521.  
  522.         return {{random_int, crushto32(hitime), stack, heap, self_data,
  523.                  self_func, exit_func, time_func, thread_id, type_id, pid,
  524.                  cpu, compile_stamp}};
  525.     }
  526.  
  527.  
  528. public:
  529.     using SeedSeq::SeedSeq;
  530.  
  531.     using base_seed_seq = SeedSeq;
  532.  
  533.     const base_seed_seq& base() const
  534.     {
  535.         return *this;
  536.     }
  537.  
  538.     base_seed_seq& base()
  539.     {
  540.         return *this;
  541.     }
  542.  
  543.     auto_seeded(default_seeds seeds)
  544.         : SeedSeq(seeds.begin(), seeds.end())
  545.     {
  546.         // Nothing else to do
  547.     }
  548.  
  549.     auto_seeded()
  550.         : auto_seeded(local_entropy())
  551.     {
  552.         // Nothing else to do
  553.     }
  554. };
  555.  
  556. using auto_seed_128 = auto_seeded<seed_seq_fe128>;
  557. using auto_seed_256 = auto_seeded<seed_seq_fe256>;
  558.  
  559.  
  560. //////////////////////////////////////////////////////////////////////////////
  561. //
  562. // uniform_distribution
  563. //
  564. //////////////////////////////////////////////////////////////////////////////
  565.  
  566. /*
  567.  * This template typedef provides either
  568.  *    - uniform_int_distribution, or
  569.  *    - uniform_real_distribution
  570.  * depending on the provided type
  571.  */
  572.  
  573. template <typename Numeric>
  574. using uniform_distribution = typename std::conditional<
  575.             std::is_integral<Numeric>::value,
  576.               std::uniform_int_distribution<Numeric>,
  577.               std::uniform_real_distribution<Numeric> >::type;
  578.  
  579.  
  580.  
  581. //////////////////////////////////////////////////////////////////////////////
  582. //
  583. // random_generator
  584. //
  585. //////////////////////////////////////////////////////////////////////////////
  586.  
  587. /*
  588.  * randutils::random_generator
  589.  *
  590.  *   An Easy-to-Use Random API
  591.  *
  592.  *   Provides all the power of C++11's random number facility in an easy-to
  593.  *   use wrapper.
  594.  *
  595.  *   In normal use, it's accessed via one of the following type aliases, which
  596.  *   also use auto_seed_256 by default
  597.  *
  598.  *       randutils::default_rng
  599.  *       randutils::mt19937_rng
  600.  *
  601.  *   It's discussed in detail at
  602.  *       http://www.pcg-random.org/posts/ease-of-use-without-loss-of-power.html
  603.  */
  604.  
  605. template <typename RandomEngine = std::default_random_engine,
  606.           typename DefaultSeedSeq = auto_seed_256>
  607. class random_generator {
  608. public:
  609.     using engine_type       = RandomEngine;
  610.     using default_seed_type = DefaultSeedSeq;
  611. private:
  612.     engine_type engine_;
  613.  
  614.     // This SFNAE evilness provides a mechanism to cast classes that aren't
  615.     // themselves (technically) Seed Sequences but derive from a seed
  616.     // sequence to be passed to functions that require actual Seed Squences.
  617.     // To do so, the class should provide a the type base_seed_seq and a
  618.     // base() member function.
  619.  
  620.     template <typename T>
  621.     static constexpr bool has_base_seed_seq(typename T::base_seed_seq*)
  622.     {
  623.         return true;
  624.     }
  625.  
  626.     template <typename T>
  627.     static constexpr bool has_base_seed_seq(...)
  628.     {
  629.         return false;
  630.     }
  631.  
  632.  
  633.     template <typename SeedSeqBased>
  634.     static auto seed_seq_cast(SeedSeqBased&& seq,
  635.                                typename std::enable_if<
  636.                                  has_base_seed_seq<SeedSeqBased>(0)>::type* = 0)
  637.                                         -> decltype(seq.base())
  638.     {
  639.         return seq.base();
  640.     }
  641.  
  642.     template <typename SeedSeq>
  643.     static SeedSeq seed_seq_cast(SeedSeq&& seq,
  644.                                    typename std::enable_if<
  645.                                      !has_base_seed_seq<SeedSeq>(0)>::type* = 0)
  646.     {
  647.         return seq;
  648.     }
  649.  
  650. public:
  651.     template <typename Seeding = default_seed_type,
  652.               typename... Params>
  653.     random_generator(Seeding&& seeding = default_seed_type{})
  654.         : engine_{seed_seq_cast(std::forward<Seeding>(seeding))}
  655.     {
  656.         // Nothing (else) to do
  657.     }
  658.  
  659.     // Work around Clang DR777 bug in Clang 3.6 and earlier by adding a
  660.     // redundant overload rather than mixing parameter packs and default
  661.     // arguments.
  662.     //     https://llvm.org/bugs/show_bug.cgi?id=23029
  663.     template <typename Seeding,
  664.               typename... Params>
  665.     random_generator(Seeding&& seeding, Params&&... params)
  666.         : engine_{seed_seq_cast(std::forward<Seeding>(seeding)),
  667.                   std::forward<Params>(params)...}
  668.     {
  669.         // Nothing (else) to do
  670.     }
  671.  
  672.     template <typename Seeding = default_seed_type,
  673.               typename... Params>
  674.     void seed(Seeding&& seeding = default_seed_type{})
  675.     {
  676.         engine_.seed(seed_seq_cast(seeding));
  677.     }
  678.  
  679.     // Work around Clang DR777 bug in Clang 3.6 and earlier by adding a
  680.     // redundant overload rather than mixing parameter packs and default
  681.     // arguments.
  682.     //     https://llvm.org/bugs/show_bug.cgi?id=23029
  683.     template <typename Seeding,
  684.               typename... Params>
  685.     void seed(Seeding&& seeding, Params&&... params)
  686.     {
  687.         engine_.seed(seed_seq_cast(seeding), std::forward<Params>(params)...);
  688.     }
  689.  
  690.  
  691.     RandomEngine& engine()
  692.     {
  693.         return engine_;
  694.     }
  695.  
  696.     template <typename ResultType,
  697.               template <typename> class DistTmpl = std::normal_distribution,
  698.               typename... Params>
  699.     ResultType variate(Params&&... params)
  700.     {
  701.         DistTmpl<ResultType> dist(std::forward<Params>(params)...);
  702.  
  703.         return dist(engine_);
  704.     }
  705.  
  706.     template <typename Numeric>
  707.     Numeric uniform(Numeric lower, Numeric upper)
  708.     {
  709.         return variate<Numeric,uniform_distribution>(lower, upper);
  710.     }
  711.  
  712.     template <template <typename> class DistTmpl = uniform_distribution,
  713.               typename Iter,
  714.               typename... Params>
  715.     void generate(Iter first, Iter last, Params&&... params)
  716.     {
  717.         using result_type =
  718.            typename std::remove_reference<decltype(*(first))>::type;
  719.  
  720.         DistTmpl<result_type> dist(std::forward<Params>(params)...);
  721.  
  722.         std::generate(first, last, [&]{ return dist(engine_); });
  723.     }
  724.  
  725.     template <template <typename> class DistTmpl = uniform_distribution,
  726.               typename Range,
  727.               typename... Params>
  728.     void generate(Range&& range, Params&&... params)
  729.     {
  730.         generate<DistTmpl>(std::begin(range), std::end(range),
  731.                            std::forward<Params>(params)...);
  732.     }
  733.  
  734.     template <typename Iter>
  735.     void shuffle(Iter first, Iter last)
  736.     {
  737.         std::shuffle(first, last, engine_);
  738.     }
  739.  
  740.     template <typename Range>
  741.     void shuffle(Range&& range)
  742.     {
  743.         shuffle(std::begin(range), std::end(range));
  744.     }
  745.  
  746.  
  747.     template <typename Iter>
  748.     Iter choose(Iter first, Iter last)
  749.     {
  750.         auto dist = std::distance(first, last);
  751.         if (dist < 2)
  752.             return first;
  753.         using distance_type = decltype(dist);
  754.         distance_type choice = uniform(distance_type(0), --dist);
  755.         std::advance(first, choice);
  756.         return first;
  757.     }
  758.  
  759.     template <typename Range>
  760.     auto choose(Range&& range) -> decltype(std::begin(range))
  761.     {
  762.         return choose(std::begin(range), std::end(range));
  763.     }
  764.  
  765.  
  766.     template <typename Range>
  767.     auto pick(Range&& range) -> decltype(*std::begin(range))
  768.     {
  769.         return *choose(std::begin(range), std::end(range));
  770.     }
  771.  
  772.     template <typename T>
  773.     auto pick(std::initializer_list<T> range) -> decltype(*range.begin())
  774.     {
  775.         return *choose(range.begin(), range.end());
  776.     }
  777.  
  778.  
  779.     template <typename Size, typename Iter>
  780.     Iter sample(Size to_go, Iter first, Iter last)
  781.     {
  782.         auto total = std::distance(first, last);
  783.         using value_type = decltype(*first);
  784.  
  785.         return std::stable_partition(first, last,
  786.              [&](const value_type&) {
  787.                 --total;
  788.                 using distance_type = decltype(total);
  789.                 distance_type zero{};
  790.                 if (uniform(zero, total) < to_go) {
  791.                     --to_go;
  792.                     return true;
  793.                 } else {
  794.                     return false;
  795.                 }
  796.              });
  797.     }
  798.  
  799.     template <typename Size, typename Range>
  800.     auto sample(Size to_go, Range&& range) -> decltype(std::begin(range))
  801.     {
  802.         return sample(to_go, std::begin(range), std::end(range));
  803.     }
  804. };
  805.  
  806. using default_rng = random_generator<std::default_random_engine>;
  807. using mt19937_rng = random_generator<std::mt19937>;
  808.  
  809. }
  810.  
  811. #endif // RANDUTILS_HPP
  812.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement