如何使用 random()={0..1} 生成任意范围内的数字并保持均匀性和密度?

psi*_*lia 5 random algorithm entropy

生成 [x..y] 范围内的随机数,其中 x 和 y 是任意浮点数。使用函数 random(),它从 P 个均匀分布的数字(称为“密度”)中返回 [0..1] 范围内的随机浮点数。必须保持均匀分布,并且 P 也必须按比例缩放。

我认为,此类问题没有简单的解决方案。为了简化一点,我问你如何在区间[-0.5 .. 0.5]中生成一个数字,然后在[0 .. 2]中,然后在[-2 .. 0]中生成一个数字,同时保持均匀性和密度?因此,对于 [0 .. 2],它必须从 P*2 个均匀分布的数字中生成一个随机数。

random() * (x - y) + y由于所有情况的密度较低,明显的简单解决方案不会生成所有可能的数字abs(x-y)>1.0。许多可能的值将被错过。请记住,random() 仅返回 P 个可能数字中的一个数字。然后,如果你将这个数字乘以 Q,它只会给出 P 个可能值中的一个,按 Q 缩放,但你也必须按 Q 缩放密度 P。

com*_*orm 2

如果您确实想生成给定范围内具有统一数字密度的所有可能的浮点数,则需要考虑浮点格式。对于二进制指数的每个可能值,您都有不同的代码数字密度。直接生成方法需要显式处理这个问题,而间接生成方法仍然需要考虑它。我将开发一种直接方法;为简单起见,以下仅指IEEE 754单精度(32 位)浮点数。

最困难的情况是任何包含零的区间。在这种情况下,为了产生完全均匀的分布,您需要将每个指数处理到最低值,再加上非标准化数字。作为一种特殊情况,您需要将零分为两种情况:+0 和 -0。

此外,如果您如此密切关注结果,则需要确保您使用的是良好的伪随机数生成器,并且具有足够大的状态空间,以便您可以期望它以接近均匀的概率命中每个值。这使 C/Unixrand()以及可能的*rand48()库函数失去资格;你应该使用像Mersenne Twister这样的东西。


关键是将目标区间分解为子区间,每个子区间都由不同的二进制指数和符号组合覆盖:在每个子区间内,浮点代码均匀分布。

第一步是选择适当的子区间,其概率与其大小成正比。如果间隔包含 0,或者以其他方式覆盖较大的动态范围,则这可能需要大量随机位,直至可用指数的整个范围。

特别是,对于 32 位 IEEE-754 数,有 256 个可能的指数值。每个指数控制的范围是下一个较大指数大小的一半,但非标准化情况除外,其大小与最小正常指数区域相同。零可以被认为是最小的非规范化数;如上所述,如果目标区间跨过零,+0 和 -0 的概率也许应该减半,以避免其权重加倍。

如果选择的子区间覆盖由特定指数控制的整个区域,则所需要做的就是用随机位(23 位,对于 32 位 IEEE-754 浮点数)填充尾数。但是,如果子区间未覆盖整个区域,则您将需要生成仅覆盖该子区间的随机尾数。

处理初始和辅助随机步骤的最简单方法可能是将目标区间舍入以包括部分覆盖的所有指数区域的整体,然后拒绝并重试落在其之外的数字。这允许以简单的 2 次方概率生成指数(例如,通过计算随机比特流中前导零的数量),并提供一种简单而准确的方法来生成仅覆盖部分数字的尾数。指数区间。(这也是处理 +/-0 特殊情况的好方法。)

作为另一个特殊情况:为了避免生成比它们所在的指数区域小得多的目标区间的低效生成,“明显简单”的解决方案实际上将为这些区间生成相当统一的数字。如果您想要完全均匀的分布,则可以通过仅使用足够的随机位来覆盖该子间隔来生成子间隔尾数,同时仍然使用上述拒绝方法来消除目标间隔之外的值。