【问题标题】:Generate random integers from random bit sequence从随机位序列生成随机整数
【发布时间】:2014-08-11 02:35:36
【问题描述】:

非常基本的问题,但我似乎无法在 Google 上找到答案。标准 PRNG 将生成一系列随机位。我将如何使用它来生成在 [0, N) 范围内具有均匀概率分布的随机整数序列?此外,每个整数都应使用(预期值)log_2(N) 位。

【问题讨论】:

标签: algorithm random language-agnostic integer prng


【解决方案1】:

如果你想要一个介于 1 和 N 之间的随机数:

  • 您计算将 N 转换为二进制数需要多少位。那是:

    n_bits = ceiling(log_2(N))
    

    其中上限是“向上取整”操作。 (例如:天花板(3)= 3,天花板(3.7)= 4)

  • 您选择随机二进制列表的前 n_bits 并将它们更改为十进制数。

  • 如果您的十进制数大于 N,那么...您将其丢弃并使用 n_bits 下一位重试,直到它起作用为止。

N = 12 的示例:

  • n_bits = 上限(log_2(12)) = 4

  • 您取随机位序列的前 4 个位,可能是“1011”

  • 你把“1011”变成一个十进制数,得到13。那是12以上,不好。所以:

  • 取随机序列中的下 4 位,可能是“1110”。

  • 将 '1110' 转换为小数,得到 7。这​​样行得通!

希望对你有帮助。

【讨论】:

  • 对于坏 N 这需要 2*ceil(log2(N)) 位。假设 N=2^10+1=1025,所以每次使用 11 位。您有大约 1/2 的拒绝机会,因此需要 2 次尝试的预期值,每次花费 11 位。
  • 是的,没错。但是,如果您选择不丢弃该序列(并对其进行更改以使其更小),则您冒着在随机选择中引入偏差的风险。但你是对的,对某些 Ns 根本没有效果。
  • @user49164 失败一次为 50%,连续失败两次为 25%、3 次、12.5% 等等...查看this 链接了解更多信息。
  • @Alexandru 正如我所说,这使得预期的尝试次数等于 2。
【解决方案2】:

实际上,大多数标准 PRNG,例如线性同余生成器或 Mersenne twister 都会生成整数值序列。即使是广义的反馈移位寄存器技术,通常也是在寄存器/字级别实现的。我不知道实际在位级别上运行的任何常用技术。这并不是说它们不存在,但它们并不常见......

生成从 1 到 N 的值通常是通过将生成的整数值取模所需的界限,然后进行接受/拒绝阶段来确保您不受模偏差的影响。例如,请参阅 Java 的 nextInt(int bound) 方法,了解如何实现它。 (将结果加 1 得到 [1,N] 而不是 [0,N-1]。)

【讨论】:

  • PRNG 通常不会在位级别操作,但是如果 PRNG 操作相对昂贵,那么我们不想浪费任何输出,因此我们必须查看位级别的输出。
  • 对于在字级别设计的算法,尝试在位级别进行操作通常会更昂贵。这就是为什么 Java 的实现只是丢弃额外的位,如果模偏差进入图片,则拒绝并继续下一个 int。你用的是哪种 PRNG 这么贵?
【解决方案3】:

理论上这是可能的。找到 a, b 使得 2^a > N^b 但非常接近。 (这可以通过迭代 log2(N) 的倍数来完成。)取第一个 a 位,并将其解释为二进制数,将其转换为基数 N(同时检查该数字是否小于 N^b)。数字给出所需序列的 b 项。

问题是转换为基数 N 非常昂贵,而且成本基本上比任何 PRNG 都要高,所以这主要是一个理论上的答案。

【讨论】:

    【解决方案4】:
    1. 计算N 所需的位数(= 值为 1 的最高有效位的位置) - 我们称之为k
    2. 从您的输入位流中取出第一个 k 位 - 我们称其为数字 X
    3. Result = X mod N
    4. 传播到下一组 k 位并从第 2 步开始重复以生成下一个随机数。

    或者,为了更好地分配,可以应用此步骤代替第 3 步:

    • 比率 = N/2k
    • 结果 = X * 比率

    【讨论】:

      【解决方案5】:

      [0, N-1] 范围开始,然后使用0s 和1s 执行二分查找:

      • 0:下半部
      • 1:上半部

      例如当 N = 16 时,您从 [0, 15] 开始,序列 0, 1, 1, 0 将给出:

      1. [0, 7]
      2. [4, 7]
      3. [6, 7]
      4. [6]

      如果 N 不是 2 的幂,那么在任何迭代中,剩余数字列表的长度可能是奇数,在这种情况下,需要决定将中间数字作为下半部分的一部分或上半部分。这可以在算法开始时决定。滚动一次:0 表示包含所有中间数字的实例到下半部分,1 表示包含所有中间数字的实例到右半部分。

      与生成log(N) 位并获取或获取mod N 的常用方法相比,我认为这至少更接近您要求的均匀分布。

      为了说明我的意思,使用我的方法在[0, 9]范围内生成一个数字:

      To generate 0
      0: 0, 0, 0, 0
      1: 0, 0, 0
      
      To generate 1
      0: 0, 0, 0, 1
      1: 0, 0, 1
      
      To generate 2
      0: 0, 0, 1
      1: 0, 1, 0
      
      To generate 3
      0: 0, 1, 0
      1: 0, 1, 1, 0
      
      To generate 4
      0: 0, 1, 1
      1: 0, 1, 1, 1
      
      To generate 5
      0: 1, 0, 0, 0
      1: 1, 0, 0
      
      To generate 6
      0: 1, 0, 0, 1
      1: 1, 0, 1
      
      To generate 7
      0: 1, 0, 1
      1: 1, 1, 0
      
      To generate 8
      0: 1, 1, 0
      1: 1, 1, 1, 0
      
      To generate 9
      0: 1, 1, 1
      1: 1, 1, 1, 1
      

      另一个简单的答案是生成一个足够大的二进制数,这样采用mod N 就不会(从统计上)偏爱某些数字而不是其他数字。但我认为您也不会喜欢这个答案,因为从您的 cmets 到另一个答案来看,您似乎在考虑生成位数方面的效率。

      简而言之,我不确定为什么我对这个答案投了反对票,因为与它使用的位数相比,这个算法似乎提供了一个很好的分布 (~log(N))。

      【讨论】:

      • 当 N 不是 2 的幂时,这不会给出均匀分布。
      • @user49164 因为如果 N 不是 2 的幂,那么在每次迭代中,剩余数字列表的长度可能是奇数,这种情况下我们不知道如何处理中间的数字,对吗?还是我错过了其他原因?
      • 在您提供的 N=10 示例中,您可以看到获得 2 和 7 的机会为 1/8,获得其他任何东西的机会为 3/32,而不是每个都有 1/10 .我认为这种级别的错误对于大多数应用程序来说是不可接受的。
      • @user49164 当 N 不是 2 的幂时,没有一个答案可以声称具有 log(N) 位的 PERFECT 均匀分布。对于我的算法,随着 N 变大,获得每个数字的机会差异会减少。我给出了我最好的答案,但显然它对于不赞成投票的人来说还不够好,而且你显然正在寻找一种算法,它可以用 log(N) 位提供完美的均匀分布。祝你好运,我也对最终答案很感兴趣。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-14
      • 2018-10-09
      • 2014-08-22
      • 1970-01-01
      • 2020-10-01
      相关资源
      最近更新 更多