Java 一种更有效的随机整数生成方法

Java 一种更有效的随机整数生成方法,java,random,Java,Random,我有一个应用程序,我需要测量一个算法消耗了多少位随机性。我通过重写Random.next(int)在调用其父方法之前递增计数器,插入了Random的子类来实现这一点 我在实现nextInt(int)方法时遇到了一些问题,因为即使范围是2的幂,它也总是会提取32位。对于其他范围,还有更多的问题:该方法是不统一的——当绘制的原始值大于小于整数的范围的最大倍数时,它只重试一次。MAX_value——并且它仍然使用了比所需更多的随机位 如何实现一个更好的nextInt(int)版本,它只使用确定范围内的

我有一个应用程序,我需要测量一个算法消耗了多少位随机性。我通过重写
Random.next(int)
在调用其父方法之前递增计数器,插入了
Random
的子类来实现这一点

我在实现
nextInt(int)
方法时遇到了一些问题,因为即使范围是2的幂,它也总是会提取32位。对于其他范围,还有更多的问题:该方法是不统一的——当绘制的原始值大于小于
整数的范围的最大倍数时,它只重试一次。MAX_value
——并且它仍然使用了比所需更多的随机位

如何实现一个更好的
nextInt(int)
版本,它只使用确定范围内的值所需的最小数量的随机位,同时保持完全一致?它不需要保证终止(无论如何都不可能),只需要概率为1的终止

编辑: 以下是我到目前为止的情况:

int nextInt(int max){
    int n = Integer.numberOfTrailingZeros(max);
    return next(n) + nextOddInteger(max >> n) << n;
}
int-nextInt(int-max){
int n=整数。跟踪零的数量(最大值);

return next(n)+nextodindinteger(max>>n)听起来您应该提取所有位,将它们放入一个持久队列中,并根据需要弹出位来消耗它们。只有在用完时才提取新的随机位


e、 g.如果Random.next抽取32位,而您需要8位,那么您只需要为每四个需要8位的调用抽取一次。

让我谈谈随机整数生成算法,这些算法在平均使用的随机位数方面是“最优”的。在本文的其余部分中,我们将假设我们有一个“true”能够产生无偏独立随机位的随机发生器

1976年,D.E.Knuth和A.C.Yao证明,任何只使用随机位产生给定概率的随机整数的算法都可以表示为二叉树,其中随机位表示遍历树和每个叶的方式(端点)与结果相对应。他们还给出了给定算法在此任务中平均需要的位数的下限。在这种情况下,统一生成
[0,n)
中整数的最佳算法最多需要
log2(n)平均+2
位。在这种意义上,有许多优化算法的示例。其中一个是J.Lumbroso(2013)(在下面实现),另一个可能是2004年给出的算法。另一方面,所有算法都不是最优的,因为它们依赖于一次生成随机位块

然而,一般来说,任何无偏的最优整数生成器都会在最坏的情况下永远运行,正如Knuth和Yao所示。回到二叉树,n个结果标签中的每一个都会留在二叉树中,因此[0,n]中的每个整数都可能以1/n的概率出现。但是如果1/n有一个非终止的二叉展开(如果n不是2的幂,则会出现这种情况),此二叉树必然-

  • 具有“无限”深度,或
  • 包括树末端的“拒绝”叶子
在任何一种情况下,算法都将在最坏的情况下永远运行,即使它平均使用很少的随机位。(另一方面,当n是2的幂时,最优二叉树将有一个有限的深度,并且没有拒绝节点。)快速掷骰机就是使用“拒绝”的算法的一个例子事件以确保其无偏见;请参阅下面代码中的注释

因此,一般来说,一个随机整数生成器可以是无偏的,也可以是常数时间(甚至两者都可以),但不能两者兼而有之。特别是,一般来说,在不引入偏差的情况下,无法“修复”不定运行时间的最坏情况。例如,模约化(例如,
randInt()%n
)相当于二叉树,在二叉树中,拒绝叶被标记的结果所取代,但由于可能的结果比拒绝叶更多,因此只有部分结果可以取代拒绝叶,从而引入偏差。如果在r一组迭代次数。(但是,根据应用程序的不同,这种偏差可能可以忽略不计。随机整数生成还有一些安全方面,这些方面过于复杂,无法在本答案中讨论。)

请注意,我们假设我们有一个随机位生成器。但是,Java实现中有一个问题:它无法生成单个随机位。(事实上,大多数伪随机数生成器一次生成一块位,而不是单个位。)因此,正如在撰写本文时的另一个答案中所提到的,为了优化随机位的使用,您需要保存
nextInt()
的结果,并一次一个地读取其位。读取完所有位后,生成另一个
nextInt()
并重复

快速骰子辊的实现
以下是快速掷骰机的JavaScript实现。请注意,它使用拒绝事件和循环来确保其无偏。
nextBit()
是一种生成独立无偏随机位的方法(例如,
Math.random()Java的
Random
类只是一个LCPRNG,例如可用的最基本类型。在*NIX上,您可以直接从
/dev/Random
中提取位,但我不建议跨平台Java使用它。@RichardJ.RossIII目前底层RNG的质量不是问题,只是需要多少位(假设它们是好的)以一致随机的方式选择整数。我最终将替换底层RNG,但我将首先处理这一部分。这里的问题不是
next(int)
在输出前丢弃的位数,而是
function randomInt(minInclusive, maxExclusive) {
  var maxInclusive = (maxExclusive - minInclusive) - 1
  var x = 1
  var y = 0
  while(true) {
    x = x * 2
    var randomBit = nextBit()
    y = y * 2 + randomBit
    if(x > maxInclusive) {
      if (y <= maxInclusive) { return y + minInclusive }
      // Rejection
      x = x - maxInclusive - 1
      y = y - maxInclusive - 1
    }
  }
}