当前位置: 首页 > 知识库问答 >
问题:

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

孙才捷
2023-03-14

这是我不懂的代码:

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

什么?通过对随机选择的n进行简单的模拟,我得到了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;
    if ((bits & 1) == 0) offset += n - nextn;
    n = nextn;
}
return offset + nextInt((int) n);

共有1个答案

贲宏硕
2023-03-14

我觉得你有点搞错了...让我试着以我所看到的方式来描述算法:

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

NextInt(int)函数用作NextLong(long)的一部分

从数学上讲,如果我们取一个数的一半,减去一半,再减去一半,再减去一半,等等,我们这样做的次数足够多,它会趋于零。同样,如果你取一个数的一半,然后把它加到一半的一半上,等等,它就会倾向于原来的数。

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

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

编辑:已添加演练-计算结果的数量较难,但从0long.max_value-1的所有long值都是可能的结果。作为“证明”,使用nextlong(0x400000000000000000)是一个简单的示例,因为所有的减半过程都是偶数的,并且它设置了63位。

由于位63被设置(这是可用的最高有效位,因为位64会使数字为负数,这是非法的),这意味着在值为<=integer.max_value之前,我们将值减半32次(这是0x000000007FFFFFF-当我们到达那里时,我们的将为0x0000000004000000)。因为减半和位移位是相同的过程,所以它认为要做的减半与最高位组和位31之间的差值一样多。63-31是32,所以我们将事物减半32倍,因此我们在while循环中执行32个循环。0x400000000000000000的初始起始值意味着,当我们将该值减半时,在这一半中只设置了一个位,并且它将沿着值“走”下去--每次通过循环时将1向右移动。

因为我谨慎地选择了初始值,所以很明显,在while循环中,逻辑实际上是在决定是否设置每个位。它接受输入值的一半(即0x200000000000000000),并决定是否将其添加到结果中。为了论证起见,我们假设所有的循环都决定在偏移量上加上一半,在这种情况下,我们从偏移量

0x2000000000000000
0x1000000000000000
0x0800000000000000
0x0400000000000000
.......
0x0000000100000000
0x0000000080000000
0x0000000040000000  (this is added because it is the last 'half' calculated)

在这一点上,我们的循环已经运行了32次,它已经“选择”添加值32次,因此在值中至少有32个状态(如果计算大/小半决定,则为64)。实际失调现在为0x3FFFFFFFC0000000(从62到31的所有位均置1)。

然后,我们调用nextInt(0x40000000),幸运的是,它会产生结果0x3FFFFFFFF,使用31位状态来实现。我们将这个值添加到我们的偏移量中,得到的结果是:

0x3fffffffffffffff

如果nextint(0x40000000)结果的“完美”分布,我们就可以完美地覆盖0x7FFFFFFFFC00000000x3FFFFFFFFFFFFFFFFFFFFFFFF的值,没有任何间隙。如果我们的while循环具有完美的随机性,我们的高位将会是0x00000000000000000x3FFFFFFC0000000的完美分布,这样就可以完全覆盖从0到0x40000000000000000的极限(不包括)

使用来自高位的32位状态,以及来自nextint(0x40000000)的(假定)最小31位状态,我们有63位状态(如果计算大半/小半决策,则更多),并且完全覆盖。

 类似资料:
  • 问题内容: 我对如何使用动作监听器和实现它们有一个想法,但是我想知道是否有人可以告诉我他们如何监听事件?有某种轮询机制吗? 问题答案: 动作侦听器使用观察者模式注册事件,主事件循环会将它们注册的所有事件通知它们。所以不,这不是轮询(拉)机制,而是相反的(推)回调。这是“不给我们打电话,我们给您打电话”编程的一个例子。因为代码中的所有内容都在单个线程(事件循环)上运行,所以您不必担心不同事件之间的同

  • 问题内容: 我试图了解Collections.binarySearch如何在Java中工作。我不太明白我得到的输出。 此代码的输出为-1。 当按此顺序插入元素时 结果是0。我认为如果找不到该元素,则结果为负数。有人可以澄清我收到的输出吗? 问题答案: 您的数据必须根据给定的比较器进行排序,以使二进制搜索能够按预期工作。(如果不是,则行为是不确定的。) 在进行此调用之前,必须根据指定的比较器(通过方

  • 问题内容: 我正在尝试了解linux syscallsched_setaffinity()的工作方式。这是我在这里提出的问题的后续。 我有本指南,该指南说明了如何使用syscall并有一个非常简洁(工作!)的示例。 因此,我下载了Linux 2.6.27.19 内核源代码。 我对包含该系统调用的行进行了“ grep”操作,得到了91个结果。没有希望。 最终,我试图了解内核如何 为特定内核 (或处理

  • 问题内容: 我刚刚了解到。它用于动态加载扩展的驱动程序。然后我们得到使用方法的连接。 那么整个事情如何运作? DriverManager类如何知道如何在不使用实际驱动程序的类名的情况下获取连接。 我们也可以将Class.forName()用于自定义应用程序…如果通过示例进行解释,我将非常高兴。 问题答案: 只需加载一个类,包括运行其静态初始化程序,如下所示: 您正在谈论的所有其余过程都是特定于JD

  • 问题内容: ArrayList在内部使用什么数据结构? 问题答案: 内部使用。 在向中添加项目时,列表会检查后备阵列是否还有剩余空间。如果有空间,则将新项目添加到下一个空白处。如果没有空间,则会创建一个更大的新阵列,并将旧阵列复制到新阵列中。 现在,还有更多空间,新元素将添加到下一个空白空间。 由于人们真的很喜欢源代码: 直接跳出JDK。

  • 问题内容: 为了清楚起见,我试图找出Collections.sort(list,new MyComp())方法如何按顺序调用compare方法。 我有一个带有雇员及其个人号码(k)的LinkedList:这些号码是:{1,2,3,4,5,6} MyComparator中的compare(Object o1,Object o2)方法返回一些数字(即与该问题无关)。sort()如何比较方法?它使用参数