ThreadLocalRandom.nextLong(long n)如何工作?

时间:2013-11-03 12:46:22

标签: java random

这是我不明白的code

// Divide n by two until small enough for nextInt. On each
// iteration (at most 31 of them but usually much less),

什么?对于随机选择的n我的琐事simulation,我可以进行32次迭代,31是平均值。

// randomly choose both whether to include high bit in result
// (offset) and whether to continue with the lower vs upper
// half (which makes a difference only if odd).

这是有道理的。

long offset = 0;
while (n >= Integer.MAX_VALUE) {
    int bits = next(2);

为这两个决定获得两位,这是有道理的。

    long half = n >>> 1;
    long nextn = ((bits & 2) == 0) ? half : n - half;

此处,nn/2.0向上或向下四舍五入,很好。

    if ((bits & 1) == 0) offset += n - nextn;
    n = nextn;

我迷路了。

}
return offset + nextInt((int) n);

我可以看到它生成了一个正确大小的数字,但它看起来相当复杂而且相当慢,我肯定不明白为什么结果应该均匀分布。 1


1 它不能真正均匀分布,因为状态仅为48位,因此它可以生成不超过2 ** 48个不同的数字。绝大多数long都无法生成,但是显示它的实验可能需要数年时间。

1 个答案:

答案 0 :(得分:2)

我认为你有点误会......让我试着用我看到的方式描述算法:

首先,假设nextInt(big)(nextInt,而不是nextLong)能够正确地生成0(包括)和big之间分布良好的值范围(不包括)。

nextInt(int)函数用作nextLong(long)

的一部分

因此,算法通过循环工作,直到值小于Integer.MAX_INT,此时它使用nextInt(int)。更有趣的是它之前做的事情......

数学上,如果我们取一半的数字,减去它的一半,然后减去一半的一半,然后减半的一半的一半,等等,我们这样做足够的时间它将趋于零。同样地,如果你拿一半的数字,并将它加到半数的一半,等等,它将倾向于原始数字。

算法在这里做的是它需要一半的数量。通过整数除法,如果数字是奇数,那么就有一个大的'一半和一小'半。算法“随机”#39;选择其中一个(大或小)。

然后,它随机选择添加一半,或者不将那一半添加到输出中。

它将数字减半并且(可能)添加一半,直到一半小于Integer.MAX_INT。此时,它只是计算nextInt(half)值并将其添加到结果中。

假设初始长限远大于Integer.MAX_VALUE,那么最终结果将获得nextInt(int)的所有好处,对于一个大的int值,至少32位状态,以及2位高于Integer.MAX_VALUE的所有高位的状态。

原始限制越大(越接近Long.MAX_VALUE),循环迭代次数越多。在最坏的情况下它会变为32次,但是对于较小的限制,它会经历较少的次数。在最坏的情况下,对于非常大的限制,您将获得用于循环的64位随机性,然后是nextInt(half)所需的任何内容。


编辑:WalkThrough添加 - 计算结果的数量更难,但是,从0Long.MAX_VALUE - 1的所有长值都是可能的结果。作为'证明'使用nextLong(0x4000000000000000)是一个很好的例子,因为所有的减半过程都是偶数,并且它有63位设置。

因为第63位已设置(最高位可用,因为bit64会使数字为负数,这是非法的),这意味着我们会在值为< = Integer.MAX_VALUE之前将值减半32(即{ {1}} - 当我们到达那里时,我们0x000000007fffffff将是half ....)因为减半和位移是相同的过程,它认为最高位集和位31之间存在尽可能多的一半.63 - 31是32,所以我们将事物减半32次,因此我们做了32次在while循环中循环。 0x0000000004000000的初始起始值意味着,当我们将值减半时,一半中只会设置一个位,并且它将会“走”'降低价值 - 每次通过循环向右移1。

因为我仔细选择了初始值,很明显在while循环中逻辑基本上决定是否设置每个位。它占用输入值的一半(即0x2000000000000000)并决定是否将其添加到结果中。让我们假设为了参数,我们所有的循环都决定将一半加到偏移量,在这种情况下,我们从偏移量0x0000000000000000开始,然后每次循环我们加一半,这意味着每次我们添加:

0x4000000000000000

此时我们的循环已经运行了32次,它已经选择了'添加值32次,因此值中至少有32个状态(如果计算大/小半决定,则为64)。实际偏移现在是0x2000000000000000 0x1000000000000000 0x0800000000000000 0x0400000000000000 ....... 0x0000000100000000 0x0000000080000000 0x0000000040000000 (this is added because it is the last 'half' calculated) (设置从62到31的所有位)。

然后,我们调用nextInt(0x40000000),幸运的是,它产生结果0x3fffffff,使用31位状态到达那里。我们将此值添加到偏移量并获得结果:

0x3fffffffc0000000

完美的&#39; 0x3fffffffffffffff 结果的分布我们可以完全覆盖nextInt(0x40000000)0x7fffffffc0000000的值,而且没有间隙。在我们的while循环中具有完美的随机性,我们的高位将是0x3fffffffffffffff0x0000000000000000的完美分布。结合从0到0x3fffffffc0000000 <的限制(不包括)的完全覆盖/ p>

高位的32位状态和0x4000000000000000的(假定的)最小31位状态,我们有63位状态(如果计算大/小半决定则更多),并且满覆盖。