Random.nextBoolean() 无论种子如何,总是返回 True
Posted
技术标签:
【中文标题】Random.nextBoolean() 无论种子如何,总是返回 True【英文标题】:Random.nextBoolean() Always Returns True No Matter the Seed 【发布时间】:2015-06-11 02:47:51 【问题描述】:当我运行以下代码时,无论我为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
,这种行为有什么原因吗?
【问题讨论】:
你认为setSeed
会做什么?
无法保证简单地将种子更改为 Random 对象会更改 nextBoolean() 返回的第一个布尔值或第二个布尔值。
不应该每次都为随机生成器创建不同的true
和false
模式吗?
没有。随机生成器需要始终为相同的种子返回相同的序列。这是最重要的属性之一。
您测试了 2^32 个可能的整数中的 10 个值,并声称“nextBoolean() 函数始终返回 true”。对于完成如此大量的研究来说,这是一个非常有力的主张。
【参考方案1】:
原因在setSeed
方法的API中找到:
由 Random 类实现的 setSeed 恰好只使用给定种子的 48 位。
事实上,您作为种子值提供的 long
乘以一个固定值(在 Random
类中私下定义),然后只考虑最低有效 48 位。即使这个乘数很大,因为您的i
值序列都是连续的,它们都会产生数值相似的种子值。因此,前几千个值实际上被视为与 nextBoolean
方法具有相同的值,并且您得到完全相同的 initial 布尔值。再次调用nextBoolean
(不再调用setSeed
)将重新乘以种子值,因此您很快就不会看到相同的模式。
如果您确实调用了setSeed
方法,您应该只需要调用一次,并且您应该在循环之外执行此操作。但是Random
类完全可以选择自己的种子值,所以我建议你根本不要调用setSeed
,除非你知道为什么要这样做。
【讨论】:
那么转换输入以使其识别为唯一数字的最佳方法是什么?我应该将i
乘以一个很大的数字吗?
@SuperNew:使用new Date().getTime() + someCustomValue
作为种子。这可确保您的种子独立于实际迭代。
除非你真的需要选择种子的值,否则就让Random
自己选择种子。换句话说,根本不要调用setSeed
,也不要为Random
构造函数提供种子值。
@Arkanon 对,Random 是自己做的。但它总是使用“0”种子。编辑:你去:)(构造)public Random() this(System.currentTimeMillis());
@dognose 确切地说:Random
类完全有能力选择自己的种子来提供与普通机器所能提供的一样多的随机性。因此,尝试手动选择种子是重复工作,并且可能比让 Java 为您做的随机性更少。【参考方案2】:
所以基本上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
(你不知道)。这就是为什么在循环中调用nextBoolean()
两次时有时会得到false
的原因。毕竟是伪随机数生成器。
顺便说一句,你真的不需要调用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;
所以,你应该把任务留给那个。
【讨论】:
【参考方案3】:这是我们称它们为“伪随机”数字的原因。每个随机调用背后都有一个复杂且通常不可逆但仍具有确定性的函数;通常是一个简单的元胞自动机。如此多的随机化种子返回 true 是这个函数的产物;为了确定你得到的东西可以说是随机的,我建议使用一个大而不同的数字,比如来自 System.nanoTime()。
【讨论】:
以上是关于Random.nextBoolean() 无论种子如何,总是返回 True的主要内容,如果未能解决你的问题,请参考以下文章