我知道您可以使用Java中的Math.random()生成从0.0到1.0的随机双精度,但计算机如何选择一个数字?计算机跟随模拟随机性的代码是什么?
答案 0 :(得分:5)
通过查看文档很容易找到,因为很高兴,这种行为在Java中得到了很好的定义。来自Math
, it says
首次调用此方法时,它会创建一个新的伪随机数生成器,就像表达式
一样
new java.util.Random()
可悲的是,它没有明确说明它调用的Random
上的哪种方法。但是,由于其返回值与Random.nextDouble
的范围相同,因此它也会链接到"另请参阅"部分,应该是一个安全的假设,即使用的是什么。
查看Random.nextDouble
, you can see
return (((long)next(26) << 27) + next(27)) / (double)(1L << 53);
以原子方式将种子更新为
(seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)
并返回
(int)(seed >>> (48 - bits))
所以最终的随机值计算为
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)
next1 = (int)(seed >>> (48 - 26))
seed = (seed * 0x5DEECE66DL + 0xBL) & ((1L << 48) - 1)
next2 = (int)(seed >>> (48 - 27))
randomValue = (((long)next1 << 27) + next2) / (double)(1L << 53);
其中
随机数生成器的种子[初始化为]一个非常可能与此构造函数的任何其他调用不同的值。
通过观察这一点,应该非常清楚的是,这个数字实际上并不是&#34;随机的&#34;因为大多数人会理解它。它是一种完全可预测的数学模式,称为pseudorandom,它可以用于多种目的。但是,可能值得研究SecureRandom
(产生不可预测的,加密强的随机值),具体取决于您希望结果的不可预测性。
答案 1 :(得分:1)
关于随机数的真相是它们并非真正随机。它们只是用当前时间以mili / nano(甚至更少) - 秒来计算...因为时间总是变化而且永远不会重复这个值不能是相同的值,因为“相同的原因”是什么使得随机编号一个随机数...
查看@FranciscoSpaeth回答:
答案 2 :(得分:0)
您可以在Java类声明中对其进行跟踪。这个来自Math.class:
public static double random() {
return RandomNumberGeneratorHolder.randomNumberGenerator.nextDouble();
}
在Eclipse中很容易完成。焦点&amp; F3。
答案 3 :(得分:0)
通过查看Java中Random类的源代码,您会发现这取决于是否使用种子-即调用public Random()或调用public Random(long seed)。
来自Java 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 * 1181783497276652981L;
if (seedUniquifier.compareAndSet(current, next))
return next;
}
}
private static final AtomicLong seedUniquifier
= new AtomicLong(8682522807148012L);
您会看到,如果未指定种子,则Random类将使用AtomicLong种子,并使用System.nanoTime()执行按位XOR操作(^)
但是,如果您指定种子,则无需调用System.nanoTime()即可调用另一部分代码。
(请注意,如果您在没有种子的情况下进行调用,则使用种子的构造函数仍会被调用,但其种子包含System.nanoTime(),从而保证每次调用时都具有不同的种子)
public Random(long seed) {
if (getClass() == Random.class)
this.seed = new AtomicLong(initialScramble(seed));
else {
// subclass might have overriden setSeed
this.seed = new AtomicLong();
setSeed(seed);
}
}
private static long initialScramble(long seed) {
return (seed ^ multiplier) & mask;
}
首先将通过调用initialScramble(long seed)方法对种子进行加扰
使用nextBytes,nextLong,nextInt等从代码中进行调用。将调用next()。其实现如下:
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
它基本上在行中包含“线性同余生成器”
nextseed = (oldseed * multiplier + addend) & mask;
您可以测试有无种子的呼叫之间的差异。如果提供种子并仅调用一次方法nextInt,nextBytes等,则每次将获得相同的值。 但是,如果不指定种子,则即使仅调用一次nextInt,nextBytes等方法,也会获得不同的值。