【问题标题】:c++ random number between two integers using WELL512使用 WELL512 的两个整数之间的 c++ 随机数
【发布时间】:2015-02-12 08:26:09
【问题描述】:

我看到这个问题可能已经在这里回答了:Random using WELL512

但是,它对用户不太友好,也没有提供如何在“现实世界”代码中使用它的示例。

这是我目前拥有的:

#define m (unsigned long)2147483647
#define q (unsigned long)127773    
#define a (unsigned int)16807
#define r (unsigned int)2836    

static unsigned long seed;    
void x_srandom(unsigned long initial_seed);
unsigned long x_random(void);    

void x_srandom(unsigned long initial_seed)
{
    seed = initial_seed;
}    

unsigned long x_random(void)
{
    int lo, hi, test;

    hi = (seed / q);
    lo = (seed % q);

    test = (a * lo - r * hi);

    if (test > 0)
        seed = test;
    else
        seed = (test + m);

    return (seed);
}

int RANDOM(int from, int to)
{
    if (from > to)
    {
        int tmp = from;
        from = to;
        to = tmp;
    }
    return ((x_random() % (to - from + 1)) + from);
}

// Real world function using RANDOM()
void testFunction()
{
    printf("A random number between 1 and 1000 is %d \r\n", RANDOM(1, 1000));
    printf("A random number between 36 and 100 is %d \r\n", RANDOM(36, 100));
    printf("A random number between 1 and 2147483647 is %d \r\n", RANDOM(1, 2147483647));
    printf("A random number between 1 and 5 is %d \r\n", RANDOM(1, 5));
}

上面的例子展示了实现它所需要知道的一切。

我想使用 WELL512 来确定我的随机数,而不是我目前的方式,按照上面的例子。

【问题讨论】:

  • 对除 2 的幂(如 1000 或 36)以外的任何值使用模数会产生有偏差的分布。你应该避免这样做。

标签: c++ random generator


【解决方案1】:

现在是时候放弃使用 % 来生成分布了。

对我来说,您应该使用 WELL512 作为统一随机数生成器(就像标准库中的 mt19937 一样)。您将它包装在一个为 result_type 公开 typedef(或 using)的类中。在您的情况下,这可能是无符号长的。然后你需要两个用于 min() 和 max() 的 constexpr。那将是 0 和 ULONG_MAX。最后,您需要公开返回单个 unsigned long 的 operator()。

之后,您可以将<random> 中的功能与您的引擎一起使用。

class well512 {
public:
    typedef unsigned long result_type;
    static constexpr result_type min() { return 0; }
    static constexpr result_type max() { return ULONG_MAX; }
    result_type operator()() { /* return some value from the underlying well512 implementation */ }
};

int main()
{
    well512 engine();
    std::uniform_int_distribution<> dist { 1, 5 };

    for (int i = 0; i != 10; ++i)
    {
        std::cout << dist(engine) << std::endl;
    }
    return 0;
}

【讨论】:

    【解决方案2】:

    这是一个完整的例子。它没有您可能想要的所有花里胡哨。例如。没有默认构造函数,也没有一个单词的构造函数。我把它留作练习。

    #include <algorithm>
    #include <array>
    #include <cstdint>
    #include <functional>
    #include <iostream>
    #include <iterator>
    #include <limits>
    #include <numeric>
    #include <ostream>
    #include <random>
    #include <vector>
    
    class seed_seq
    {
    public:
        template <typename InputIterator>
        seed_seq(InputIterator first, InputIterator last)
        {
            for (; first != last; ++first)
            {
                v.push_back(*first);
            }
        }
    
        template <typename RandomAccessIterator>
        void generate(RandomAccessIterator first, RandomAccessIterator last)
        {
            std::vector<unsigned int>::size_type i = 0;
            for (; first != last; ++first)
            {
                *first = v[i];
                if (++i == v.size()){ i = 0; }
            }
        }
    
    private:
        std::vector<unsigned int> v;
    };
    
    class well512
    {
    public:
        using result_type = unsigned int;
    
        static result_type min() { return 0; }
        static result_type max() { return std::numeric_limits<std::uint32_t>::max(); }
    
        static const unsigned int state_size = 16;
    
        explicit well512(seed_seq& sequence) : index(0)
        { sequence.generate(std::begin(state), std::end(state)); }
    
        result_type operator()()
        {
            std::uint32_t z0 = state[(index + 15) & 0x0fU];
            std::uint32_t z1 = xsl(16, state[index]) ^ xsl(15, state[(index + 13) & 0x0fU]);
            std::uint32_t z2 = xsr(11, state[(index + 9) & 0x0fU]);
            state[index] = z1 ^ z2;
            std::uint32_t t = xslm(5, 0xda442d24U, state[index]);
            index = (index + state_size - 1) & 0x0fU;
            state[index] = xsl(2, z0) ^ xsl(18, z1) ^ (z2 << 28) ^ t;
    
            return state[index];
        }
    
    private:
        // xor-shift-right
        std::uint32_t xsr(unsigned int shift, std::uint32_t value)
        { return value ^ (value >> shift); }
    
        // xor-shift-left
        std::uint32_t xsl(unsigned int shift, std::uint32_t value)
        { return value ^ (value << shift); }
    
        // xor-shift-left and mask
        std::uint32_t xslm(unsigned int shift, std::uint32_t mask, std::uint32_t value)
        { return value ^ ((value << shift) & mask); }
    
        unsigned int index;
        std::array<std::uint32_t, state_size> state;
    };
    
    int main()
    {
        // Use a random device to generate 16 random words used as seed for the well512 engine
        std::random_device rd;
    
        std::vector<well512::result_type> seed_data;
        std::generate_n(std::back_inserter(seed_data), well512::state_size, std::ref(rd));
    
        seed_seq sequence(std::begin(seed_data), std::end(seed_data));
    
        // Create a well512 engine
        well512 engine(sequence);
    
        // Now apply it like any other random engine in C++11
        std::uniform_int_distribution<> dist{ 1, 6 };
        auto rand = std::function <int()> { std::bind(std::ref(dist), std::ref(engine)) };
    
        // Print out some random numbers between 1 and 6 (simulating throwing a dice)
        const int n = 100;
        std::generate_n(std::ostream_iterator<int>(std::cout, " "), n, rand);
        std::cout << std::endl;
    
        return 0;
    }
    

    【讨论】:

      【解决方案3】:

      喜欢这个用户 515430 吗?

      #define m  (unsigned long)2147483647
      
      #define W 32
      #define R 16
      #define P 0
      #define M1 13
      #define M2 9
      #define M3 5
      
      #define MAT0POS(t,v) (v^(v>>t))
      #define MAT0NEG(t,v) (v^(v<<(-(t))))
      #define MAT3NEG(t,v) (v<<(-(t)))
      #define MAT4NEG(t,b,v) (v ^ ((v<<(-(t))) & b))
      
      #define V0            STATE[state_i                   ]
      #define VM1           STATE[(state_i+M1) & 0x0000000fU]
      #define VM2           STATE[(state_i+M2) & 0x0000000fU]
      #define VM3           STATE[(state_i+M3) & 0x0000000fU]
      #define VRm1          STATE[(state_i+15) & 0x0000000fU]
      #define VRm2          STATE[(state_i+14) & 0x0000000fU]
      #define newV0         STATE[(state_i+15) & 0x0000000fU]
      #define newV1         STATE[state_i                 ]
      #define newVRm1       STATE[(state_i+14) & 0x0000000fU]
      
      #define FACT 2.32830643653869628906e-10
      
      static unsigned int state_i = 0;
      static unsigned int STATE[R];
      static unsigned int z0, z1, z2;
      
      void InitWELLRNG512a(unsigned int *init){
          int j;
          state_i = 0;
          for (j = 0; j < R; j++)
              STATE[j] = init[j];
      }
      
      double WELLRNG512a(void){
          z0 = VRm1;
          z1 = MAT0NEG(-16, V0) ^ MAT0NEG(-15, VM1);
          z2 = MAT0POS(11, VM2);
          newV1 = z1                  ^ z2;
          newV0 = MAT0NEG(-2, z0) ^ MAT0NEG(-18, z1) ^ MAT3NEG(-28, z2) ^ MAT4NEG(-5, 0xda442d24U, newV1);
          state_i = (state_i + 15) & 0x0000000fU;
          return ((double)STATE[state_i]) * FACT;
      }
      
      int RANDOM(int from, int to)
      {
          if (from > to)
          {
              int tmp = from;
              from = to;
              to = tmp;
          }
      
          return to + (from - to) * (WELLRNG512a() / (long double)m);
      }
      

      【讨论】:

      • 没关系。这只是每次输出最大的数字。 RANDOM(1, 100) 总是得到 100。(回到绘图板)
      【解决方案4】:

      获得两个值之间均匀分布的随机整数的最简单方法是使用浮点数学。

      double get_uniform_rand() {
          /*Assumes unsigned 32 bit return value from myrnd in range 0 - 0xFFFFFFFF*/
          return (double)myrnd() / (double)0xFFFFFFFF;
      }
      
      int32_t get_rnd_in_range(int32_t l, int32_t h) {
          return (int32_t)((double)l + get_uniform_rand() * (double)(h-l));
      }
      

      这更像是一种 C 方法,就像提到的 user515430 一样,有一种标准的 C++ 方法可以做到这一点(尽管我个人没有使用过)。

      【讨论】:

        猜你喜欢
        • 2013-10-14
        • 2012-04-25
        • 1970-01-01
        • 2018-09-18
        • 1970-01-01
        • 2015-01-22
        • 2015-11-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多