【问题标题】:Why 1103515245 is used in rand?为什么 1103515245 用在 rand 中?
【发布时间】:2012-01-24 01:01:24
【问题描述】:

我说的是 this 从 C 标准中对 rand() 的简单实现:

static unsigned long int next = 1;

int rand(void)  /* RAND_MAX assumed to be 32767. */
{
    next = next * 1103515245 + 12345;
    return (unsigned)(next/65536) % 32768;
}

this Wikipedia article我们知道乘数a(在上面的代码a = 1103515245)应该只满足两个条件:

  1. a - 1 可以被 m 的所有质因数整除。
    (在我们的例子中 m = 2^32 是 int 的大小,所以 m 只有一个素因子 = 2)
  2. 如果m 是 4 的倍数,a - 1 是 4 的倍数。
    (32768 是 4 的倍数,1103515244 也是)

为什么他们选择了这样一个奇怪的、难以记住的“伙计,我受够了这些随机数字,随便写”数字,比如 1103515245?

也许有一些明智的理由,这个数字比另一个更好?

例如,为什么不设置a = 20000000001?它更大、更酷、更容易记住。

【问题讨论】:

  • @Ed S.:足够合理的问题可以要求解释一个神奇的数字......
  • :) 当然不是,但是看看数字 12345。一旦他们选择了简单、好看的数字 12345,一旦选择了不好的……没有理由? :)
  • 您可以先查看参考资料,答案可能就在某处:en.wikipedia.org/wiki/Linear_congruential_generator#References
  • 这很有趣。我没有解决方案,但可能与 65536 是可以存储在两个字节上的最大 int 有关。
  • @Edwin: 通常65536 是不能存储在 2 个字节上的最小 int ;)

标签: c random standards


【解决方案1】:

如果使用 LCG 在 d 维空间上绘制点,它们最多位于 (d!m)1/d支持>超平面。这是 LCG 的一个已知缺陷。

如果您不仔细选择 a 和 m(超出完全周期性的条件),它们可能位于比这少得多的平面上。这些数字是通过所谓的光谱测试选择的。

“谱检验”(名称来自数论)是 d 维联合分布所在的连续超平面之间的最大距离。您希望它尽可能小,以便测试尽可能多的 d。

有关该主题的历史回顾,请参阅 this paper。请注意,论文中提到了您引用的生成器(作为 ANSIC),并且确定不是很好。然而,高阶 16 位是可以接受的,但是许多应用程序将需要超过 32768 个不同的值(正如您在 cmets 中指出的那样,周期确实是 2^31 - 维基百科链接中完全周期性的条件可能只是必要的)。

ANSI 文档中的原始源代码没有采用高 16 位,生成的生成器很差,很容易被误用(rand() % n 是人们首先想到在0 和@ 之间绘制一个数字987654324@,在这种情况下,这会产生非常非随机的结果。

另请参阅有关数字配方中的 LCG 的讨论。引用:

更糟糕的是,许多早期的生成器恰好变得特别糟糕 m 和 a 的选择。一个臭名昭著的例程,RANDU,a = 65539 m = 231,多年来在 IBM 大型计算机上广泛使用, 并广泛复制到其他系统上。我们中的一个人回忆起毕业生 学生制作了一个只有 11 架飞机的“随机”情节并被告知 被他误用的计算机中心的编程顾问 随机数生成器:“我们保证每个数字都是随机的 单独,但我们不保证其中一个以上 随机的。”这使我们的研究生教育至少推迟了一年!

【讨论】:

    【解决方案2】:

    请记住,rand()uniform distribution 的近似值。使用这些数字是因为它们已经过测试,表明它们生成的分布看起来更均匀。

    鉴于可表示范围内有大量无符号整数对,我怀疑是否有人尝试过所有有效种子。如果你认为你有更好的参数选择,那就试试吧!你有代码,只需将LCG 的参数分解出来并运行测试。生成一堆数字(比如 1000 万),计算生成数字的直方图并绘制它以查看分布。

    编辑 如果您对开发用于实际应用程序的伪随机数生成器感兴趣,我建议您阅读有关该主题的大量文献。上面给出的“建议”只是为了帮助表明选择​​任意的“更大、更酷、更容易记住”的 LCG 参数会给出非常差的分布。 /编辑

    此外,它是一个库函数,我从未见过使用标准库版本rand() 来记住其 LCG 参数的程序。

    【讨论】:

    • 在尝试参数时,你必须知道你在寻找什么,尤其是关于连续数字的联合分布(这对许多 LCG 参数来说很糟糕,而对少数几个参数来说就不那么糟糕了) .这方面有大量文献。
    • @DonalFellows:我不建议任何人在 PRNG 开发中使用这种简单的方法,我认为这不是 OP 想要的。见鬼,我不建议一开始就使用 LCG。但是,这个答案足够清楚地解释了为什么 C 的 rand() 使用“难以记住”的 LCG 参数而不是“更大、更酷且更容易记住”的参数。
    • 一般来说,PRNG 分为三类:简单的(例如rand())、科学的(具有非常好的光谱特性)和密码的(其中每个比特都必然难以预测)尽可能)。有大量关于这方面的文献 - 确实有很多研究 - 只使用好的文献很重要,因为它很容易出错。
    • 很抱歉,但我仍然看不到反对票背后的原因。如果 OP 要求关于如何开发随机数生成器的真正建议,我不会按原样回答。这是对一个简单问题的简单回答。无论如何,我添加了一条注释,提到不要使用它来开发自定义 PRNG。
    【解决方案3】:

    早期的计算倾向于关注位和字节,并使用寄存器来最小化代码字节(在行之前有字节)

    我只在下面找到了一个合理的线索:

    这个生成器的输出不是很随机。如果我们使用上面列出的样本生成器,那么 16 个关键字节的序列将是高度非随机的。例如,rand() 的每个连续输出的低位将交替出现(例如,0,1,0,1,0,1,...)。你明白为什么吗? x * 1103515245的低位与x的低位相同,然后加上12345只是翻转低位。因此低位交替。这将可能的键集缩小到只有 2113 种可能性;远低于 2128 的期望值。

    http://inst.eecs.berkeley.edu/~cs161/fa08/Notes/random.pdf

    还有两个合理的答案:

    改进一个糟糕的随机数生成器 (1976) by Bays, Durham Bays, Carter, S D Durham

    http://en.wikipedia.org/wiki/TRNG

    【讨论】:

      【解决方案4】:

      这个数字看起来很特别,它只是在两个素数之间:P。

      现在说正经的,看看它是不是一个好的选择,看看输出。即使翻转一个位,您也会看到非常不同的结果。

      另外,考虑一下您期望的可预测性...这种实现很糟糕,您可以考虑一个更强大但更简单的替代方案,例如FNV-1a

      【讨论】:

      • 好吧,我想反驳这个概念,您如何定义 PRNG?
      • PRNG 就是为此目的而设计的。散列算法只需要是一个单向函数,如果你循环它,你可能会得到一个相当糟糕的随机数来源。哈希算法不一定会指定一种将其循环起来以供 PRNG 使用的方法。
      • @KubaOber 所以...你的定义在哪里?
      • 哈希函数是一个函数 h : {0,1}^* -> {0,1}^k 用于一些固定的输出长度 k 和任意输入长度。 PRNG 是一个函数 f : {0,1}^s -> {0,1}^s x {0,1}^k 对于一些固定的种子长度 s 和输出长度 k。您可以使用散列函数来实现 PRNG,但您没有指定您的构造或给出任何说明为什么这是一件好事(它可能不是,但也不是使用 LCG)。跨度>
      猜你喜欢
      • 2015-08-29
      • 2022-01-13
      • 1970-01-01
      • 2018-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-01
      相关资源
      最近更新 更多