【问题标题】:create a random sequence, skip to any part of the sequence创建一个随机序列,跳到序列的任何部分
【发布时间】:2010-05-04 21:41:28
【问题描述】:

在 Linux 中。有一个 srand() 函数,您可以在其中提供一个种子,它将保证在随后对 random() 函数的后续调用中具有相同的伪随机数序列。

比方说,我想通过记住这个种子值来存储这个伪随机序列。

另外,假设我稍后想要这个伪随机序列中的千分之十的数字。

一种方法是使用 srand() 提供种子编号,然后调用 random() 10 万次,并记住该编号。

有没有更好的方法可以跳过伪随机列表中的所有其他 99,999 个数字,直接获取列表中的第 100 个数字。

谢谢,

【问题讨论】:

  • 请注意srand 种子randsrandom 种子random
  • 我很想知道这背后的用例 - 我想我遗漏了一些东西,因为我不明白为什么第 100,000 个序列比第一个更随机。
  • @HorusKol 已经有一段时间了,但是当对相关的东西感兴趣时通过谷歌结束了这里,我确实想给你一个用例。想象一个游戏,它为您提供一系列谜题,每个谜题都由一个数字生成。为了确保每个人得到不同的谜题,您使用随机生成器并记住初始种子。现在,您可能希望用户能够从他离开的地方继续(此时种子是已知的,这使事情变得更容易),甚至只要他记住它的编号就可以重播任何谜题。
  • (是的,我不得不稍微扩展一下以获得完整的用例。但是,这个想法不是关于“更随机”,而是关于“重新创建”一些“随机”的东西(因此实际上将随机性的伪部分变成了优势)

标签: math random srand


【解决方案1】:

我不确定是否有在任何平台上实施rand 的明确标准;但是,从GNU Scientific Library 中选择这个:

——生成器:gsl_rng_rand

这是 BSD rand 生成器。它的顺序是

xn+1 = (a xn + c) mod m

a = 1103515245,c = 12345 和 m = 231。种子指定初始值 x1。该生成器的周期为 231,每个生成器使用 1 个字的存储空间。

所以要“知道”xn,您需要知道 xn-1。除非我遗漏了一些明显的模式,否则你不能在不计算它之前的所有值的情况下跳转到一个值。 (但并非每个rand 实现都如此。)

如果我们从 x1...

开始
  • x2 = (a * x1 + c) % m
  • x3 = (a * ((a * x1 + c) % m) + c) % m
  • x4 = (a * ((a * ((a * x1 + c) % m) + c) % m) + c) % m
  • x5 = (a * (a * ((a * ((a * x1 + c) % m) + c) % m) + c ) % m) + c) % m

很快就会失控。该功能是否易于简化?我不这么认为。

(有一个系列的统计短语,其中 xn 取决于 xn-1 -- 谁能提醒我这个词是什么?)

【讨论】:

  • RE:统计短语。 1 阶马尔可夫链?虽然在这种情况下并非如此,因为 x_n 并不独立于 x_(n-2)。
  • 这不是马尔可夫链。它基本上只是整数模 2^31 的递归关系。
  • 顺便说一句,递归关系很容易解决。请记住,您可以在最后将所有这些 % m 操作拉出到 % m 的单个应用程序中。由此产生的关系是一个教科书的例子。但是将其转换为高效且可用的代码需要更多的工作,因为天真地应用它需要任意精度的整数。但我认为可能有办法部分控制这种情况。
【解决方案2】:

如果它们在您的系统上可用,您可以使用rand_r 代替randsrand,或者使用initstatesetstaterandomrand_runsigned * 作为参数,并在其中存储其状态。多次调用rand_r后,保存这个无符号整数的值,下次作为起始值。

对于random(),使用initstate 而不是srandom。为要恢复的任何状态保存状态缓冲区的内容。要恢复状态,请使用 setstate 填充缓冲区并调用。如果缓冲区已经是当前状态缓冲区,则可以跳过对setstate 的调用。

【讨论】:

    【解决方案3】:

    这是使用 BSD rand() 函数从@Mark 的答案开发的。

    rand1() 计算第 n 个随机数,从 seed 开始,逐步执行 n 次。

    rand2() 使用快捷方式计算相同的值。一次最多可以步进 2^24-1 步。在内部,它只需要 24 个步骤。

    如果 BSD 随机数生成器对你来说足够好,那么这就足够了:

    #include <stdio.h>
    
    const unsigned int m = (1<<31)-1;
    
    unsigned int a[24] = {
        1103515245, 1117952617, 1845919505, 1339940641, 1601471041,
        187569281 , 1979738369, 387043841 , 1046979585, 1574914049,
        1073647617, 285024257 , 1710899201, 1542750209, 2011758593,
        1876033537, 1604583425, 1061683201, 2123366401, 2099249153,
        2051014657, 1954545665, 1761607681, 1375731713
    };
    
    unsigned int b[24] = {
        12345, 1406932606, 1449466924, 1293799192, 1695770928, 1680572000,
        422948032, 910563712, 519516928, 530212352, 98880512, 646551552,
        940781568, 472276992, 1749860352, 278495232, 556990464, 1113980928,
        80478208, 160956416, 321912832, 643825664, 1287651328, 427819008
    };
    
    unsigned int rand1(unsigned int seed, unsigned int n)
    {
        int i;
        for (i = 0; i<n; ++i)
        {
            seed = (1103515245U*seed+12345U) & m;
        }
        return seed;
    }
    
    unsigned int rand2(unsigned int seed, unsigned int n)
    {
        int i;
        for (i = 0; i<24; ++i)
        {
            if (n & (1<<i))
            {
                seed = (a[i]*seed+b[i]) & m;
            }
        }
        return seed;
    }
    
    int main()
    {
        printf("%u\n", rand1 (10101, 100000));
        printf("%u\n", rand2 (10101, 100000));
    }
    

    适应任何线性同余生成器并不难。我用一种具有适当整数类型的语言 (Haskell) 计算了这些表,但我可以在 C 中用另一种方式计算它们,只用几行代码。

    【讨论】:

      【解决方案4】:

      如果您总是想要第 100,000 个项目,只需将其存储起来以备后用。

      或者您可以生成序列并将其存储...并稍后通过索引查询特定元素。

      【讨论】:

        猜你喜欢
        • 2018-11-19
        • 2010-09-15
        • 1970-01-01
        • 2023-04-04
        • 2012-12-13
        • 2023-04-05
        • 2010-10-16
        • 2010-11-19
        相关资源
        最近更新 更多