如何从位流中获取两个值之间的整数?

时间:2012-06-05 12:04:50

标签: php

我有一个项目使用php的mt_rand()来生成不同的随机整数,但我最近获得了对真实随机位流的访问。我无法弄清楚如何创建一个类似于mt_rand()的函数,在这里我可以从我的比特流中得到两个值之间的随机整数。我怎样才能做到这一点?

3 个答案:

答案 0 :(得分:1)

我只会阅读PHP_INT_SIZE * 8位并将所得到的数字压缩到您需要的范围内:

function squash($nr, $min, $max)
{
    return $min + $nr % ($max - $min);
}

另一种方式:

function squash($nr, $min, $max)
{
    return $min + round($nr / PHP_INT_MAX * ($max - $min));
}

我刚刚想到这一点,为什么不使用你的随机流并将其推入mt_srand()

function squash($nr, $min, $max)
{
    mt_srand($nr);
    return mt_rand($min, $max);
}

答案 1 :(得分:1)

我提出了一种方法,但不确定它在使用的位方面是否是最有效的方式(代码未经测试,只是演示理论):

function RandomInteger($min, $max)
{
    $range = ($max - $min) + 1;
    $bitsNeeded = ceil( log($range, 2) );
    $number = ReadBitsAndConvertToInteger($bitsNeeded);
    if ($number < $range)
        return $number + $min;
    else if ($number > $range)
        return RandomInteger($min, $max);
}

正如您所看到的,函数将有可能重复,这意味着将使用更多的位,这就是为什么我不确定它是否是最有效的方式,就所使用的位而言。

Pr(repeat) = (2^bitsNeeded - range) / (2^bitsNeeded)

Therefore, 0 < Pr(repeat) < 0.5

因此,在最糟糕的情况下,不得不重复的几率几乎为0.5,它开始变得不太可能需要重复超过10次左右,平均值不到2次。显然,在不得不重复的机会较低时,这些数字会降低。

答案 2 :(得分:1)

让我说说随机整数生成算法,就其平均使用的随机位数而言,它们是“最佳”的。在本文的其余部分,我们将假定我们有一个“真实的”随机生成器,该生成器可以生成无偏且独立的随​​机位。

1976年,DE Knuth和AC Yao表明,任何仅使用随机位以给定概率生成随机整数的算法都可以表示为二叉树,其中随机位指示遍历树和每片叶子的方式(端点)对应于结果。他们还给出了给定算法平均需要多少位才能完成此任务的下限。在这种情况下,用于均匀地在[0, n)中生成整数的最优算法平均平均最多需要log2(n) + 2位。在这种意义上,有许多 optimized 算法的示例。其中一个是J. Lumbroso(2013)的Fast Dice Roller(在下面实现),另一个可能是2004年Math Forum中给出的算法。另一方面,所有算法{{3 }}并非最佳选择,因为它们一次只能生成随机位块。

但是,任何最优整数生成器(也都是无偏)通常会在最坏的情况下永远运行,如Knuth和Yao所示。回到二叉树,n个结果标签中的每一个都留在二叉树中,以便[0,n)中的每个整数都可以1 / n的概率出现。但是,如果1 / n具有不间断的二进制扩展(如果n不是2的幂,就会是这种情况),那么该二进制树必然是其中一个-

  • 具有“无限”的深度,或者
  • 在树的末端
  • 包括“拒绝”叶子,

,无论哪种情况,即使平均使用很少的随机位,该算法也将在最坏的情况下永远运行。 (另一方面,当n为2的幂时,最佳二叉树将具有有限的深度且没有拒绝节点。)快速骰子滚子是使用“拒绝”事件以确保其无偏的算法示例;请参阅下面的代码中的注释。

因此,通常,随机整数生成器可以是 无偏的恒定时间(或什至都不是),。 >特别是,没有办法在不引入偏差的情况下“修复”不确定运行时间的最坏情况。例如,模减少(例如mt_rand() % n)等效于二叉树,其中拒绝叶子用标记的结果替换-但是由于拒绝叶子的可能性更大,因此只有部分结果可以代替拒绝叶子,造成偏见。如果您在设置一定数量的迭代后停止拒绝,则会产生相同类型的二叉树和相同类型的偏差。 (但是,根据应用的不同,这种偏见可以忽略不计。随机整数生成还存在安全方面的问题,这些问题太复杂了,无法在此答案中讨论。)

请注意,我们假设我们有一个随机位发生器。如果您的答案可以通过ReadBitsAndConvertToInteger(1)来实现。

快速骰子滚子实现

以下是Fast Dice Roller的JavaScript实现。请注意,它使用拒绝事件和循环来确保它没有偏见。 ReadBitsAndConvertToInteger(1)是一个随机位生成器(例如,您的答案中所使用的)。

function randomInt(minInclusive, maxExclusive) {
  var maxInclusive = (maxExclusive - minInclusive) - 1
  var x = 1
  var y = 0
  while(true) {
    x = x * 2
    var randomBit = ReadBitsAndConvertToInteger(1)
    y = y * 2 + randomBit
    if(x > maxInclusive) {
      if (y <= maxInclusive) { return y + minInclusive }
      // Rejection
      x = x - maxInclusive - 1
      y = y - maxInclusive - 1
    }
  }
}