重新映射到新范围后保持均匀分布

时间:2015-03-25 14:17:48

标签: random probability discrete-mathematics prng probability-density

由于这是关于将统一分发重新映射到具有不同范围的另一个分布,因此虽然我使用PHP,但这不是PHP问题。

我有一个加密安全随机数生成器,可以在0PHP_INT_MAX之间为我提供均匀分布的整数(均匀离散分布)。

如何以有效的方式重新映射这些结果以适应不同的范围?

目前我正在使用$mappedRandomNumber = $randomNumber % ($range + 1) + $min $range = $max - $min,但是由于该范围内的第一个PHP_INT_MAX%$range整数有更高的机会被选中,因此显而易见无效分布的均匀性。

3 个答案:

答案 0 :(得分:0)

好吧,对PHP一无所知肯定会让我成为专家,所以

精神上转换为浮动U [0,1)

f = r / PHP_MAX_INT

然后做

mapped = min + f*(max - min)

回到整数

mapped = min + (r * max - r * min)/PHP_MAX_INT

如果计算是通过64位数学运算完成的,而PHP_MAX_INT是2 ^ 31则应该可以正常工作

答案 1 :(得分:0)

这就是我最终要做的事情。 PRNG 101(如果它不适合,忽略并再次生成)。不是很复杂,但很简单:

public function rand($min = 0, $max = null){

  // pow(2,$numBits-1) calculated as (pow(2,$numBits-2)-1) + pow(2,$numBits-2) 
  // to avoid overflow when $numBits is the number of bits of PHP_INT_MAX
  $maxSafe = (int) floor(
    ((pow(2,8*$this->intByteCount-2)-1) + pow(2,8*$this->intByteCount-2))   
    / 
    ($max - $min)
  ) * ($max - $min);

  // discards anything above the last interval N * {0 .. max - min -1} 
  // that fits in {0 ..  2^(intBitCount-1)-1}
  do {
    $chars = $this->getRandomBytesString($this->intByteCount);
    $n = 0;
    for ($i=0;$i<$this->intByteCount;$i++) {$n|=(ord($chars[$i])<<(8*($this->intByteCount-$i-1)));}
  } while (abs($n)>$maxSafe);

  return (abs($n)%($max-$min+1))+$min;

}

欢迎任何改进。

https://github.com/elcodedocle/cryptosecureprng/blob/master/CryptoSecurePRNG.php上的完整代码)

答案 2 :(得分:0)

这是我要怎么做的草图:

请考虑您在[A, B)范围内具有均匀的随机整数分布,这是您的随机数生成器提供的。 让L = B - A。 令P为2的最高幂,使得P <= L。 假设X是该范围内的样本。 首先计算Y = X - A。 如果为Y >= P,则将其丢弃并从新的X开始,直到获得适合的Y

现在Y包含log2(P)个均匀随机的位-零将其扩展到log2(P)位。

现在我们有了统一的随机位发生器,可以根据需要提供任意数量的随机位。

要生成目标范围内的数字,请将[A_t, B_t)作为目标范围。让L_t = B_t - A_t。 令P_t为2的最小幂,以使P_t >= L_t。 读取log2(P_t)个随机位并从中得到一个整数,我们将其称为X_t。 如果为X_t >= L_t,请将其丢弃,然后重试,直到获得合适的数字为止。 您期望范围内的随机数将为L_t + A_t

实现注意事项:如果您的L_tL是2的幂,则您不必丢弃任何东西。如果没有,那么即使在最坏的情况下,您平均也应该在少于2次的试验中得到正确的数字。