也就是说,使用某些特定的upperBound
参数,它永远不会连续产生超过16个偶数:
Random random = new Random();
int c = 0;
int max = 17;
int upperBound = 18;
while (c <= max) {
int nextInt = random.nextInt(upperBound);
boolean even = nextInt % 2 == 0;
if (even) {
c++;
} else {
c = 0;
}
}
在此示例中,代码将永远循环,而当
upperBound
例如为16时,它将 swift 终止。这种行为的原因可能是什么?该方法的javadoc中有一些注释,但我无法理解它们。
UPD1 :该代码似乎以奇数个上限终止,但可能卡在偶数上限
UPD2 :
我按照注释中的建议修改了代码以捕获
c
的统计信息:Random random = new Random();
int c = 0;
long trials = 1 << 58;
int max = 20;
int[] stat = new int[max + 1];
while (trials > 0) {
while (c <= max && trials > 0) {
int nextInt = random.nextInt(18);
boolean even = nextInt % 2 == 0;
if (even) {
c++;
} else {
stat[c] = stat[c] + 1;
c = 0;
}
trials--;
}
}
System.out.println(Arrays.toString(stat));
现在,它尝试在行中达到偶数偶数-以获取更好的统计信息,并且偶数仍然是偶数。
结果竟然令人惊讶:
[16776448, 8386560, 4195328, 2104576, 1044736,
518144, 264704, 132096, 68864, 29952, 15104,
12032, 1792, 3072, 256, 512, 0, 256, 0, 0]
首先,它会按预期减少2倍,但请注意最后一行!在这里,它变得疯狂,所捕获的统计数据似乎完全奇怪。
这是对数刻度的条形图:
20
如何获得upperBound
值256次是另一个谜团 最佳答案
http://docs.oracle.com/javase/6/docs/api/java/util/Random.html:
它是一个伪随机数生成器。这意味着您实际上并没有掷骰子,而是使用公式根据当前随机值来计算下一个“随机”值。为了产生随机的错觉,使用了seed
。种子是与公式一起使用以生成随机值的第一个值。
显然,javas的随机实现(“公式”)连续生成的偶数不超过16个。
此行为是通常用时间初始化seed
的原因。深入启动程序时,您会得到不同的结果。
这种方法的好处是可以产生可重复的结果。如果您有一个生成“随机” map 的游戏,例如,您想再次播放它,就可以记住种子来重新生成同一 map 。
对于真正的随机数,某些操作系统提供了特殊的设备,这些设备会根据外部事件(例如鼠标移动或网络流量)生成“随机性”。但是我不知道如何利用Java。
从Java文档 secureRandom :
请注意,secureRandom不会也不保证可以保证真正的随机数。
为什么换种子无济于事
假设随机数的范围仅为0-7。
现在,我们使用以下公式生成下一个“随机”数字:
next = (current + 3) % 8
该序列变为
0 3 6 1 4 7 2 5
。如果现在使用种子
3
,您要做的就是更改起点。在仅使用先前值的简单实现中,每个值只能在序列包装环绕并再次开始之前出现一次。否则会有无法到达的部分。
例如。想象序列
0 3 6 1 3 4 7 2 5
。数字0,4,7,2 and 5
永远不会被生成一次以上(深入种子,它们可能永远不会生成),因为一旦序列循环3,6,1,3,6,1,...。简化的伪随机数生成器可以认为是该范围内所有数字的排列,您可以将种子用作起点。如果它们更高级,则必须将置换替换为可能多次出现相同编号的列表。
更复杂的生成器可以具有内部状态,允许同一编号在序列中出现几次,因为状态使生成器知道从何处继续。