簡體   English   中英

glibc rand函數實現

[英]glibc rand function implementation

我正在閱讀帶有glibc源代碼的標准庫rand()函數實現 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;
}

我不明白random_r如何生成隨機數(buf->rand_type != TYPE_0) ,有誰請解釋一下? 謝謝。

glibc rand()有兩種不同的生成器實現:

  1. 簡單的線性同余生成器(LCG),由以下等式定義:

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

    & 0x7fffffff拋棄最隨機最重要的位)

    這是一個非常簡單的單狀態LCG。 它有一些缺點。 最重要的一點是,因為它是單個狀態生成器,所以它不會在每個單獨的rand()調用上生成完全偽隨機數。 它真正做的是它以偽隨機順序遍歷整個范圍(2 ^ 31) 這有一個有意義的含義:當你獲得一些數字時,這意味着你不會在當前時期再次獲得這個數字。 您將在下一次2 ^ 31 rand()調用中再次獲得該數字,不久之后,不會更晚。

    該生成器在glibc源中稱為TYPE_0

  2. 一個稍微先進的附加反饋發生器。 該生成器具有許多狀態,這意味着它沒有上述的“遍歷屬性”。 您可以在同一時期內獲得相同的數字兩次(或更多次)。

    您可以在此處找到該算法的絕佳描述。

    該生成器在glibc源中稱為TYPE_1TYPE_2TYPE_3TYPE_4

    回到你的問題,這就是它產生價值的方式:

     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; } 

    你問題中else之后的代碼只是上面的代碼,但是以不同的方式編寫(使用指向包含先前值的數組的指針)。

使用哪個生成器取決於使用initstate()函數設置的初始狀態的大小。 第一個(LCG)生成器僅在狀態大小為8個字節時使用。 當它更大時,使用第二個發生器。 使用srand()設置種子時,默認情況下狀態的大小為128字節,因此使用第二個生成器。 在您的問題中引用的glibc源文件中,所有內容都寫在注釋中。

如果其他人需要對GNU C Library的srand()/ rand()函數進行簡單的重新實現,這個C#類會准確地再現生成的隨機數。 unchecked關鍵字是顯式允許整數溢出。 (根據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