给定一个真正的随机数生成器,每次调用输出1或0,如何使用它从任意范围中选择一个数字



如果我有一个真正的随机数生成器(TRNG),每次调用它时都能给我一个0或1,那么生成长度等于2幂的范围内的任何数字都是微不足道的。例如,如果我想生成一个0到63之间的随机数,我只需轮询TRNG 5次,最大值为11111,最小值为00000。问题是当我想要一个不等于2^n的整数时。假设我想模拟掷骰子。我需要一个介于1和6之间的范围,权重相等。很明显,我需要三个比特来存储结果,但轮询TRNG三次会引入两个旧值。我们可以简单地忽略它们,但这样一来,骰子的一侧被掷的几率就会低得多。

我的一些问题最有效地解决了这个问题。

获得完全准确结果的最简单方法是拒绝采样。例如,生成一个从1到8的随机值(3位),每当得到7或8时,就会拒绝并生成一个新值(3个新位)。循环执行此操作。

你可以通过生成大量的比特,进行mod 6,并带着偏见生活,来任意地关闭。在像32位值mod 6这样的情况下,偏差将非常小,以至于几乎不可能检测到,即使在模拟了数百万次滚动之后也是如此。

如果您想要一个0范围内的数字。。R-1,选取最小n使得R小于或等于2n。然后生成一个范围为0的随机数r。。2n-1使用您的方法。如果它大于或等于R,则丢弃它并重新生成。你的生成以这种方式失败的概率最多为1/2,平均不到两次尝试,你就会得到一个在你想要的范围内的数字。这种方法是平衡的,不会以任何方式损害结果的随机性。

正如您所观察到的,您可以通过连接比特来通过2的幂将可能的随机值的范围重复地加倍,但如果您从整数比特(如零)开始,则无法获得除2以外的任何素数因子的范围。

有几种出路;没有一个是理想的:

  1. 只需生成第一个大于所需范围的可到达范围,如果随机值超出所需范围,则丢弃结果并重新开始
  2. 产生一个非常大的范围,并尽可能均匀地分布在你想要的输出中,忽略你得到的小偏差
  3. 产生一个非常大的范围,将你能均匀分布在你想要的输出中,如果你遇到了分布均匀的集合之外的少数值中的一个,那么放弃结果,重新开始
  4. 与3一样,但回收值中未转换为结果的部分

第一种选择并不总是一个好主意。数字2和3很常见。如果你的随机比特很便宜,那么3通常是最快的解决方案,重复的几率很小。

对于最后一个;假设您在[0.31]中建立了一个随机值r,并且需要由此产生一个结果x[0.5]。[0,29]中的r的值可以使用mod 6映射到所需的输出,而没有任何偏差,而[30,31]的值必须放在地板上以避免偏差。

在前一种情况下,您会产生一个有效的结果x,但还剩下一些随机性——范围[0,5]、[6,11]等之间的差异(在这种情况下有五个可能的值)。您可以使用它开始为您需要生成的下一个随机值构建新的r

在后一种情况下,您没有得到任何x,必须重试,但您不必丢弃所有r。从非法范围[30,31]中选择的特定值将保留下来,可以自由用作下一个r的起始值(两个可能的值)。

从那一点开始,你的随机范围不必是2的幂。这并不意味着它会神奇地达到你当时需要的范围,但它确实意味着你可以尽量减少你扔掉的东西。

r越大,如果溢出,可能需要丢弃的比特就越多,但发生这种情况的可能性就越小。添加一个比特可以使风险减半,但成本仅线性增加,因此最好使用您能处理的最大r

相关内容

最新更新