简体   繁体   English

glibc rand函数实现

[英]glibc rand function implementation

I'm reading c standard library rand() function implementation with glibc source code. 我正在阅读带有glibc源代码的标准库rand()函数实现 stdlib/random_r.c, line 359 stdlib / random_r.c,第359行

int
__random_r (buf, result)
            struct random_data *buf;
            int32_t *result;
{
  int32_t *state;

  if (buf == NULL || result == NULL)
    goto fail;

  state = buf->state;

  if (buf->rand_type == TYPE_0)
    {
      int32_t val = state[0];
      val = ((state[0] * 1103515245) + 12345) & 0x7fffffff;
      state[0] = val;
      *result = val;
    }
  else
    {
      int32_t *fptr = buf->fptr;
      int32_t *rptr = buf->rptr;
      int32_t *end_ptr = buf->end_ptr;
      int32_t val;

      val = *fptr += *rptr;
      /* Chucking least random bit.  */
      *result = (val >> 1) & 0x7fffffff;
      ++fptr;
      if (fptr >= end_ptr)
        {
          fptr = state;
          ++rptr;
        }
      else
        {
          ++rptr;
          if (rptr >= end_ptr)
            rptr = state;
        }
      buf->fptr = fptr;
      buf->rptr = rptr;
    }
  return 0;

 fail:
  __set_errno (EINVAL);
  return -1;
}

I don't understand how random_r generate random number when (buf->rand_type != TYPE_0) , anyone please explain? 我不明白random_r如何生成随机数(buf->rand_type != TYPE_0) ,有谁请解释一下? Thanks. 谢谢。

glibc rand() has two different generator implementations: glibc rand()有两种不同的生成器实现:

  1. A simple linear congruential generator (LCG), defined by the following equation: 简单的线性同余生成器(LCG),由以下等式定义:

    val = ((state * 1103515245) + 12345) & 0x7fffffff

    ( & 0x7fffffff throws away the least random most significant bit) & 0x7fffffff抛弃最随机最重要的位)

    This is a very simple, single state LCG. 这是一个非常简单的单状态LCG。 It has some drawbacks. 它有一些缺点。 The most important one is that, because it is a single state generator, it does not generate a fully pseudorandom number on each separate rand() call. 最重要的一点是,因为它是单个状态生成器,所以它不会在每个单独的rand()调用上生成完全伪随机数。 What it really does is that it traverses the whole range (2^31) in a pseudorandom order . 它真正做的是它以伪随机顺序遍历整个范围(2 ^ 31) This has a meaningful implication: when you obtain some number it means that you will not obtain that number again in the present period. 这有一个有意义的含义:当你获得一些数字时,这意味着你不会在当前时期再次获得这个数字。 You will obtain that number again exactly in the next 2^31 rand() call, no sooner, no later. 您将在下一次2 ^ 31 rand()调用中再次获得该数字,不久之后,不会更晚。

    This generator is called the TYPE_0 in the glibc source. 该生成器在glibc源中称为TYPE_0

  2. A slightly more advanced, additive feedback generator. 一个稍微先进的附加反馈发生器。 That generator has many states, which means that it does not have the "traverse property" described above. 该生成器具有许多状态,这意味着它没有上述的“遍历属性”。 You can get the same number twice (or more times) during the same period. 您可以在同一时期内获得相同的数字两次(或更多次)。

    You can find an excellent description of that algorithm here . 您可以在此处找到该算法的绝佳描述。

    This generator is called the TYPE_1 , TYPE_2 , TYPE_3 or TYPE_4 in the glibc source. 该生成器在glibc源中称为TYPE_1TYPE_2TYPE_3TYPE_4

    Coming back to your question, that is how it generates values: 回到你的问题,这就是它产生价值的方式:

     seeding_stage() // (code omitted here, see the description from above link) for (i=344; i<MAX; i++) { r[i] = r[i-31] + r[i-3]; val = ((unsigned int) r[i]) >> 1; } 

    The code after the else in your question is simply the above code, but written in a different way (using pointers to the array containing previous values). 你问题中else之后的代码只是上面的代码,但是以不同的方式编写(使用指向包含先前值的数组的指针)。

Which generator is used depends on the size of the initial state set with the initstate() function. 使用哪个生成器取决于使用initstate()函数设置的初始状态的大小。 The first (LCG) generator is used only when state size is 8 bytes. 第一个(LCG)生成器仅在状态大小为8个字节时使用。 When it is bigger, the second generator is used. 当它更大时,使用第二个发生器。 When you set your seed using srand() the size of the state is 128 bytes by default, so the second generator is used. 使用srand()设置种子时,默认情况下状态的大小为128字节,因此使用第二个生成器。 Everything is written in comments in the glibc source file referenced by you in your question. 在您的问题中引用的glibc源文件中,所有内容都写在注释中。

In case anyone else needs a simple reimplementation of the GNU C Library's srand()/rand() functions, this C# class reproduces the generated random numbers exactly. 如果其他人需要对GNU C Library的srand()/ rand()函数进行简单的重新实现,这个C#类会准确地再现生成的随机数。 The unchecked keyword is to explicitly allow integer overflow. unchecked关键字是显式允许整数溢出。 (Based on Piotr Jurkiewicz's answer.) (根据Piotr Jurkiewicz的回答。)

public class GnuRand
{
    private uint[] r;
    private int n;

    public GnuRand(uint seed)
    {
        r = new uint[344];

        unchecked
        {
            r[0] = seed;
            for (int i = 1; i < 31; i++)
            {
                r[i] = (uint)((16807 * (ulong)r[i - 1]) % 2147483647);
            }
            for (int i = 31; i < 34; i++)
            {
                r[i] = r[i - 31];
            }
            for (int i = 34; i < 344; i++)
            {
                r[i] = r[i - 31] + r[i - 3];
            }
        }

        n = 0;
    }

    public int Next()
    {
        unchecked
        {
            uint x = r[n % 344] = r[(n + 313) % 344] + r[(n + 341) % 344];
            n = (n + 1) % 344;
            return (int)(x >> 1);
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM