简化(即退出并发)Random.next(int bits)
看起来像
protected int next(int bits) {
seed = (seed * multiplier + addend) & mask;
return (int) (seed >>> (48 - bits));
}
使用掩码将种子减少到48位。为什么它比仅仅
更好protected int next(int bits) {
seed = seed * multiplier + addend;
return (int) (seed >>> (64 - bits));
}
?我已经阅读了很多关于随机数的内容,但是没有理由这样做。
答案 0 :(得分:5)
原因是低位往往具有较低的周期(至少在Java使用的算法中)
来自Wikipedia - Linear Congruential Generator:
如上所示,LCG并不总是使用它们产生的值中的所有位。 Java实现在每次迭代时产生48位,但仅从这些值返回32个最高有效位。这是因为高阶位具有比低阶位更长的周期(见下文)。使用这种技术的LCG比没有使用这种技术的产生更好的价值。
编辑:
在进一步阅读之后(方便地,在维基百科上),a,c和m的值必须满足这些条件才能拥有整个期间:
c和m必须是相对素数
a-1可被m
如果m是4
我能清楚地说出的唯一一个仍然是#3。需要检查#1和#2,我感觉这些中的一个(或两个)都失败了。
答案 1 :(得分:2)
来自java.util.Random顶部的文档:
因此整个算法设计用于操作48位种子,而不是64位种子。我想你可以和Knuth先生一起讨论; p
答案 2 :(得分:0)
来自wikipedia(@ helloworld922发布的引用所引用的引用):
LCG的另一个问题是,如果m被设置为2的幂,则生成序列的低阶位具有比整个序列短得多的周期。通常,第n个最低有效位。输出序列的基数b表示,其中bk = m表示某个整数k,重复最多为句点bn。
此外,它继续(我的斜体):
当m是2的幂时,LCG的低阶位不应该依赖于任何程度的随机性。实际上,简单地用2n代替模数项表明低阶位经历非常短的周期。特别是,当m是2的幂时,任何全周期LCG将产生交替的奇数和偶数结果。
最后,原因可能是历史性的:Sun的人们希望能够可靠地工作,而Knuth公式给出了32个重要位。请注意,java.util.Random
API说明了这一点(我的斜体):
如果使用相同的种子创建了两个Random实例,并且为每个实例创建了相同的方法调用序列,则它们将生成并返回相同的数字序列。 为了保证此属性,为Random类指定了特定的算法。为了Java代码的绝对可移植性,Java实现必须使用此处显示的所有算法Random。但是,Random类的子类允许使用其他算法,只要它们遵守一般合同对于所有方法。
因此我们坚持将其作为参考实现。但是,这并不意味着您不能使用其他生成器(以及子类Random或创建新类):
来自同一维基百科页面:
Donald Knuth的MMIX m = 2 64 a = 6364136223846793005 c = 1442695040888963407
你有一个64位的公式。
随机数很棘手(如Knuth所说),根据您的需要,如果需要64位数字,只需调用java.util.Random
两次并连接这些位就可以了。如果您真的关心统计属性,请使用类似Mersenne Twister的内容,或者如果您关心信息泄漏/不可预测性,请使用java.security.SecureRandom
。
答案 3 :(得分:0)
看起来没有良好这样做的原因。 使用经过验证的设计,应用掩模是一种保守的方法。 离开它最有可能导致更好的发电机,然而,在不了解数学的情况下,这是一个冒险的步骤。
屏蔽的另一个小优势是8位架构的速度增益,因为它使用6个字节而不是8个字节。