java中随机数生成器的说明
阅读费舍尔·耶茨的维基百科页面上的最后一段内容: 最后,需要注意的是,即使使用完美的随机数生成,也可能因不正确使用生成器而在实现中引入缺陷。例如,假设Java实现在不传递构造函数参数的情况下,为每个对shuffler的调用创建一个新的生成器。然后,生成器将按照语言的时间(对于Java,为System.currentTimeMillis())进行默认种子设定。因此,如果两个调用者在小于时钟粒度的时间跨度内调用洗牌器(对于Java来说是一毫秒),那么它们创建的生成器将是相同的,并且(对于相同长度的数组)将生成相同的排列。如果快速连续多次调用洗牌器,这几乎肯定会发生,在这种情况下会导致分布极不均匀;它还可以应用于来自不同线程的独立调用。更健壮的Java实现将使用在shuffler函数外部定义的生成器的单个静态实例 除了这一段的最后一句,我什么都懂。当作者说: 更健壮的Java实现将使用在shuffler函数外部定义的生成器的单个静态实例java中随机数生成器的说明,java,random,Java,Random,阅读费舍尔·耶茨的维基百科页面上的最后一段内容: 最后,需要注意的是,即使使用完美的随机数生成,也可能因不正确使用生成器而在实现中引入缺陷。例如,假设Java实现在不传递构造函数参数的情况下,为每个对shuffler的调用创建一个新的生成器。然后,生成器将按照语言的时间(对于Java,为System.currentTimeMillis())进行默认种子设定。因此,如果两个调用者在小于时钟粒度的时间跨度内调用洗牌器(对于Java来说是一毫秒),那么它们创建的生成器将是相同的,并且(对于相同长度的数
这意味着您只有一个静态函数,如下所示:
class RandomUtil {
public static final Random rand = new Random();
}
然后使用这个rand生成器
RandomUtil.rand.nextInt()
生成所有随机数。您只使用这一个实例,并确保不会出现wiki中提到的问题。使用相同种子和相同排列的两个随机数生成器可能比使用一个生成器生成不同的随机数提供更少的随机性
假设您在同一时刻创建了两个默认随机数生成器。他们使用时间戳(毫秒)作为种子。并且,将有相同的排列。现在,您尝试从两个不同的生成器中获取第一个随机数,很可能它们是相同的
为避免出现这种情况,请创建一个
公共静态最终随机变量
并在任何地方使用它。为使此实现健壮,并且不可能生成可预测的随机数,应在shuffler类之外的静态方法中定义generator类。因此,在任何给定时间都不会创建生成器的两个或多个对象。从而最大限度地减少生成相同随机数的可能性。文章的一部分是错误的:
例如,假设Java实现在不传递构造函数参数的情况下,为每个对shuffler的调用创建一个新的生成器。然后,生成器将按照语言的时间(对于Java,为System.currentTimeMillis())进行默认种子设定。因此,如果两个调用者在小于时钟粒度的时间跨度内调用洗牌器(对于Java来说是一毫秒),那么它们创建的生成器将是相同的,并且(对于相同长度的数组)将生成相同的排列
事实并非如此。虽然为每个调用创建一个新的生成器是一种不好的做法,但是Random
类包含专门用于防止这种情况的代码:
public Random() {
this(++seedUniquifier + System.nanoTime());
}
private static volatile long seedUniquifier = 8682522807148012L;
首先,分辨率是一纳秒,而不是一毫秒。创建发电机并使用一次通常需要一纳秒以上的时间。其次,即使没有,每次调用的seedUniquifier
值也会不同
因此,虽然在每次调用时生成一个新的
随机仍然是一个坏主意,但它的危害比那篇文章所建议的要小一些。IMO,作者说,如果我们使用一个实例(即单例模式),问题(前面讨论过)就不会发生。我不会这样做。它取消了用SecureRandom
或其他RNG替代默认Random
的功能(大多数洗牌器,包括JDK中的洗牌器,都支持此功能。)