由于这是关于将统一分发重新映射到具有不同范围的另一个分布,因此虽然我使用PHP,但这不是PHP问题。
我有一个加密安全随机数生成器,可以在0
和PHP_INT_MAX
之间为我提供均匀分布的整数(均匀离散分布)。
如何以有效的方式重新映射这些结果以适应不同的范围?
目前我正在使用$mappedRandomNumber = $randomNumber % ($range + 1) + $min
$range = $max - $min
,但是由于该范围内的第一个PHP_INT_MAX%$range
整数有更高的机会被选中,因此显而易见无效分布的均匀性。
答案 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_t
和L
是2的幂,则您不必丢弃任何东西。如果没有,那么即使在最坏的情况下,您平均也应该在少于2次的试验中得到正确的数字。