即,它永远不会连续生成超过16个偶数,并带有一些特定的upperBound
参数:
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时,代码将很快终止。
这种行为的原因是什么?方法的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));
现在它尝试在行中达到20
个平均值 - 以获得更好的统计信息,而upperBound
仍然是18
。
结果结果不仅令人惊讶:
[16776448, 8386560, 4195328, 2104576, 1044736,
518144, 264704, 132096, 68864, 29952, 15104,
12032, 1792, 3072, 256, 512, 0, 256, 0, 0]
首先它按预期减少2倍,但请注意最后一行!在这里它变得疯狂,捕获的统计数据似乎非常奇怪。
以下是对数刻度的条形图:
c
如何获得价值17
256次又是另一个谜团
答案 0 :(得分:5)
http://docs.oracle.com/javase/6/docs/api/java/util/Random.html:
此类的实例用于生成流的 伪随机数。该类使用48位种子,该种子经过修改 使用线性同余公式。 (见唐纳德克努特,艺术 计算机编程,第3卷,第3.2.1节。)
如果使用相同的种子创建了两个Random实例,那么 为每个方法调用相同的方法调用,它们将生成和 返回相同的数字序列。 [...]
它是伪 - 随机数生成器。这意味着您实际上并没有掷骰子,而是使用公式根据当前随机值计算下一个“随机”值。为了产生随机化的假象,使用seed
。种子是公式中用于生成随机值的第一个值。
显然javas随机实现(“公式”),连续生成的偶数不超过16个。
这种行为是seed
通常随时间初始化的原因。根据你何时启动你的程序,你会得到不同的结果。
这种方法的好处是可以产生可重复的结果。如果你有一个游戏生成“随机”地图,你可以记住种子,如果你想再次播放它,重新生成相同的地图。
对于真正的随机数,某些操作系统会提供特殊设备,这些设备会从鼠标移动或网络流量等外部事件中生成“随机性”。但是我不知道如何利用java。
来自 secureRandom 的Java文档:
许多SecureRandom实现采用伪随机的形式 数字生成器(PRNG),这意味着它们使用确定性 算法从真随机种子产生伪随机序列。 其他实现可能产生真正的随机数,还有其他实现 可以结合使用这两种技术。
请注意,secureRandom也可以不保证 true 随机数。
假设随机数只有0-7。 现在我们使用以下公式生成下一个“随机”数字:
next = (current + 3) % 8
序列变为0 3 6 1 4 7 2 5
。
如果你现在拿种子3
你所做的就是改变起点。
在这个仅使用前一个值的简单实现中,每个值只能在序列换行并再次启动之前出现一次。否则会有一个无法到达的部分。
E.g。想象一下序列0 3 6 1 3 4 7 2 5
。数字0,4,7,2 and 5
永远不会产生多次(因为它们可能永远不会产生种子),因为一旦序列循环3,6,1,3,6,1,......。
可以将简化的伪随机数生成器视为范围内所有数字的排列,并使用种子作为起点。如果它们更高级,则必须使用可能多次出现相同数字的列表替换排列。
更复杂的生成器可以具有内部状态,允许在序列中多次出现相同的数字,因为状态允许生成器知道继续的位置。
答案 1 :(得分:2)
Random的实现使用简单的线性同余公式。这些公式具有自然周期性和它们产生的序列中的各种非随机模式。
你所看到的是这些模式之一的人工制品......没有任何故意。这不是偏见的一个例子。相反,它是auto-correlation的一个例子。
如果您需要更好(更“随机”)的数字,则需要使用SecureRandom
而不是Random
。
“为什么以这种方式实施的答案是” ......性能。对Random.nextInt
的调用可以在数十或数百个时钟周期内完成。对SecureRandom
的调用可能至少慢2个数量级,可能更多。
答案 2 :(得分:1)
为了便于移植,Java指定实现必须使用java.util.Random的劣质LCG方法。对于像复杂模拟或蒙特卡罗方法这样的随机数的任何严重使用,这种方法是完全不可接受的。使用具有更好的PRNG算法的附加库,如Marsaglia的MWC或KISS。 Mersenne Twister和Lagged Fibonacci发电机通常都可以。
我确信这些算法都有Java库。我有一个带有Java绑定的C库,如果它适合你:ojrandlib。