How good is java.util.Random?
两个问题:
我要为我放入的每一粒种子得到不同的数字序列吗?
有"死"的种子吗?(产生零或很快重复的一个。)
顺便问一下,如果有的话,我应该使用哪种其他prng?
解决方案:因为,我将使用prng制作一个游戏,我不需要它是加密安全的。我要和旋转木马一起去,因为它的速度和周期都很长。
在某种程度上,随机数生成器是用于课程的马。随机类使用合理选择的参数实现LCG。但仍具有以下特点:
- 相当短的时期(2^48)
- 比特不是同样随机的(见我关于比特位置随机性的文章)
- 只会产生一小部分数值组合(著名的"落在平面上"的问题)
如果这些事情对您不重要,那么Random具有作为JDK一部分提供的可取功能。这对休闲游戏(但不是涉及金钱的游戏)来说已经足够好了。没有软弱的种子。
另一种替代方案是XORHSHIFT生成器,它可以在Java中实现如下:
1 2 3 4 5 6 | public long randomLong() { x ^= (x << 21); x ^= (x >>> 35); x ^= (x << 4); return x; } |
对于一些非常便宜的操作,这有一个2^64-1的周期(不允许为零),并且非常简单,可以在重复生成值时进行内联。各种换档值都是可能的:有关更多详细信息,请参阅George Marsaglia关于xorshift生成器的论文。您可以将生成的数字中的位视为等随机的。一个主要的缺点是,它偶尔会进入一个"常规",在这个数字中设置的位不多,然后需要几代人才能摆脱这种常规。
其他可能性包括:
- 组合不同的发电机(例如,将Xorshift发电机的输出输入到LCG中,然后将结果添加到具有不同参数的Xorshift发电机的输出中):这通常允许"消除不同方法的缺点",并且如果组合发电机的周期是小心的,则可以给出更长的周期。奥森
- 添加一个"滞后"(以给出更长的周期):本质上,在生成器通常会转换最后生成的数字的地方,存储一个"历史缓冲区"并转换(n-1023)th。
我要说的是,避免使用愚蠢的内存来给你一个比你真正需要的时间长的周期(有些周期比宇宙中原子的数量大——你通常不需要这个周期)。注意,"长周期"并不一定意味着"高质量发电机"(尽管2^48仍然有点低!).
正如zvrba所说,javadoc解释了正常的实现。伪随机数生成器上的维基百科页面具有相当数量的信息,并提到Melxne Twitter,它不被认为是加密安全的,但是非常快,并且在Java中有各种实现。(最后一个链接有两个实现——我相信还有其他的实现。)
如果您需要密码安全生成,请阅读维基百科页面-有各种可用选项。
正如RNG所说,Sun的实现肯定不是一种状态,但对于大多数目的来说已经足够好了。如果您需要随机数用于加密目的,则有JavaSuriTy.SoCurrand,如果您只需要更快、更好的JavaUTIL随机,那么很容易在网络上找到MelShanne Wistor的Java实现。
文档中对此进行了描述。线性同余发生器在理论上被很好地理解,并且在文献和互联网上有很多关于它们的资料。具有相同参数的线性同余生成器总是输出相同的周期序列,seed唯一决定的是序列的起始位置。所以第一个问题的答案是"是的,如果你产生足够的随机数。"
在我的博客中看到答案:
http://code-o-matic.blogspot.com/2010/12/how-long-is-period-of-random-numbers.html
随机状态有一个最大的周期(很长,即2^64周期)。这可以直接推广到2^k-投资任意多的状态位,得到最大周期。相比之下,龙卷风的周期实际上非常短(见上述博客帖子中的评论)。
--哎呀。Random将自身限制为48位,而不是使用长整型的64位,因此相应地,它的周期毕竟是2^48,而不是2^64。
如果RNG质量真的对你很重要,我建议你使用你自己的RNG。也许java.util.random非常棒,在这个版本中,在您的操作系统上,等等,它可能就是。但这可能会改变。以前发生过这样的事,一个图书馆作家在后来的版本中把事情搞得更糟。
写自己的东西很简单,然后你就知道到底发生了什么。它不会在升级等方面改变。这里是一个10分钟内可以移植到Java的生成器。如果你一周后开始用新语言写作,你可以重新移植它。
如果您不实现自己的代码,您可以从一个著名的源代码中获取一个著名的RNG代码,并在您的项目中使用它。那就没人会从你下面把发电机换掉。
(我不主张人们想出自己的算法,只有自己的实现。大多数人,包括我自己,都没有业务开发自己的算法。写一个你认为很棒的坏生成器很容易。这就是为什么人们需要问这样的问题,想知道库生成器有多好。我引用的生成器中的算法已经通过了许多同行评审的振铃器。)