当我运行以下代码时,无论我使用for
循环的范围,代码总是打印出true
十次。
public static void main(String[] args)
{
Random bool = new Random();
for (int i = 0; i < 10; i++) {
bool.setSeed(i);
System.out.println(bool.nextBoolean());
}
}
但是,如果我对代码稍作更改并让随机生成器在打印前运行nextBoolean()
函数一次,我会在true
和false
中正常分布当我改变for循环的范围时,输出变化:
public static void main(String[] args)
{
Random bool = new Random();
for (int i = 0; i < 10; i++) {
bool.setSeed(i);
bool.nextBoolean(); //Only change
System.out.println(bool.nextBoolean());
}
}
在我看来,nextBoolean()
函数在第一次执行时总是返回true
,这种行为有什么理由吗?
答案 0 :(得分:6)
原因可以在setSeed
方法的API中找到:
按类Random实现setSeed恰好只使用给定种子的48位。
实际上,您提供的long
作为种子值乘以固定值(在Random
类中私有定义),然后仅考虑最低有效48位。即使这个乘数很大,因为你的i
值序列都是连续的,它们都会产生数值相似的种子值。因此,前几千个值实际上被视为与nextBoolean
方法具有相同的值,并且您获得完全相同的初始布尔值。再次调用nextBoolean
(不再调用setSeed
)将重新传输种子值,因此您可以快速远离看到相同的模式。
如果你确实调用setSeed
方法,你只需要调用一次,你应该在循环之外这样做。但是Random
类完全有能力选择自己的种子值,所以我建议你不要打{{1}},除非你知道为什么要这样做。
答案 1 :(得分:1)
所以基本上nextBoolean
方法只能返回true
或false
。 seed
值的总数可以是[Long.MIN_VALUE, Long.MAX_VALUE]
。所以,你可以假设这些种子的一半你会得到true
而另一半你会得到false
。
现在,当您迭代10个数字时,对于这10个种子,您获得的值可能是true
。当您尝试相当大的范围时,您更有可能获得两个值的平均分配。
现在每次调用nextBoolean()
时,种子都会使用(seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)
更新为其他值。因此,如果当前种子为1
,则下一个种子将为25214903916
,您可以在其中获得true
或false
(您不知道)。这就是为什么有时当你在循环中调用false
两次时nextBoolean()
。毕竟,它是伪随机数生成器。
setSeed()
方法。该方法仅用于将种子重置为特定值。 Random类实例本身将以种子值开始,并在每次从中获取值时更新它。你不必担心它。
如果你看到Random
类的代码,这就是他们第一次分配种子的方式:
public Random() {
this(seedUniquifier() ^ System.nanoTime());
}
private static long seedUniquifier() {
// L'Ecuyer, "Tables of Linear Congruential Generators of
// Different Sizes and Good Lattice Structure", 1999
for (;;) {
long current = seedUniquifier.get();
long next = current * 181783497276652981L;
if (seedUniquifier.compareAndSet(current, next))
return next;
}
}
所以,你应该把任务留给那个。
答案 2 :(得分:0)
这是我们称之为&#34; psuedorandom&#34;数字。每次随机通话背后都有一个复杂的,通常是不可逆的,但仍然是确定性的功能;通常是一个简单的细胞自动机。对于这么多随机化种子,返回true是这个函数的一个人工产物;为了确保你得到的东西可以说是随机的,我建议使用一个大的变量号,例如来自System.nanoTime()。