如何在恒定时间内生成无偏随机bigint

时间:2015-10-22 18:26:02

标签: random language-agnostic real-time bigint

在我的嵌入式项目中,我有一个biginteger类来处理任意长度的整数。我希望能够生成0和任意数字之间的随机bigint。假设我有一个随机字节的质量来源。

我见过的所有实现基本上都做同样的事情:

  1. 使用正确的字节数生成一个大数字
  2. 如果大于最大值,则再次生成。
  3. 我看到这个实现的问题是它可能需要很长时间。想象一下max = 2^2049-1 =(01 FF .. FF)。该算法将生成257个字节,然后检查最重要的字节是<=1。因此,有一个254/256的机会必须生成一个全新的257字节数。在(不可否认的)最坏的情况下,这个循环可以持续几分钟或几年。

    我的问题是:
    在生成的数字太大的情况下,有没有办法保留我已生成的大部分字节?
    重新生成最重要的字节是否有效,或者是否会引入偏差?将结果右移一位数怎么样?

    有没有办法让时间具有确定性,同时还能避免偏见?

    -

    另一个边缘情况:max = 2^2048 + 1 =(01 00 .. 01)在这种情况下,如果剩余字节为0后跟00或{{1},则最高有效字节可以为非零}}。所以大多数情况下,如果MSB非零,那么它将无效,只是重新生成该字节永远不会使其有效。但是强制将其设置为零似乎也是错误的。

3 个答案:

答案 0 :(得分:1)

答案是,通常不可能在恒定时间内在[0,n)中生成随机无偏整数。一个值得注意的例外是,当RNG产生随机位并且n为2的幂时。

例如,假设RNG是一个“真”随机发生器,并且可以产生无偏的随机位。然后,除非n为2的幂,否则只有两种可能的处理方式:

  • 它可以使用模减少(或Lemire的乘移移位减少)。这将在固定时间内运行,但是会产生偏差(某些数字的产生可能性会比其他数字高一些)。
  • 它可以使用拒绝采样。这不会引起偏差,但可以在最坏的情况下永久运行(即使它具有预期的恒定时间复杂度)。这类算法有很多种,其中包括模减少和拒绝步骤(如果n不是2的幂,则是必需的),以及Fast Dice Roller(使用随机位) 。

(有关这两种算法的概述,请参见我的“ A Note on Integer Generation Algorithms”部分。)

从这个意义上讲,Knuth和Yao在1976年证明,任何仅使用随机位以给定概率生成随机整数的算法都可以表示为二叉树,其中随机位指示遍历树的方式,每个叶(端点)对应于结果。在这种情况下,[0,n)中的每个整数都可以1 / n的概率出现。而且,如果1 / n具有不终止的二进制扩展(如果n不是2的幂,就是这种情况),那么该二进制树必然是-

  • 具有“无限”的深度,或者
  • 在树的末端
  • 包括“拒绝”叶子,

无论哪种情况,该算法都不会在恒定时间内运行。

模数减少或类似的减少等同于二叉树,其中拒绝叶子用标记的结果替换-但是由于拒绝叶子的可能性更大,因此只有部分结果可以代替拒绝叶子,从而产生偏差。如果您在设置一定数量的迭代后停止拒绝,则会产生相同类型的二叉树和相同类型的偏差。 (另请参见L. Devroye于1986年发表的《非均匀随机变量生成》第15章。)

因此:通常,整数生成器可以是 无偏恒定时间,但不能同时是两者。

如果您不能忍受永远运行的最坏情况,那么您唯一可以做的就是设置固定的最大拒绝数或使用减少项,这两种情况都会导致偏差。但是,根据您的应用程序,这种偏差可以忽略不计(例如,出于应用程序的目的,如果算法“失败”的机会与它“成功”的机会相比可以忽略不计)。随机整数生成还涉及安全方面,这些问题太复杂了,无法在此答案中讨论。

答案 1 :(得分:0)

如果您的任意最大数量是2减1的幂,则可以使用随机比特源(例如抛硬币)来填充比特。这给出了具有均匀分布的数字。您可以使用高质量的RNG生成32或64组的位,并在没有偏差的情况下截断最后一个字。

现在,如果您的任意最大数不是2减1的幂,请使用上述技术在0..1范围内创建均匀分数。用于分数的位越多,结果的偏差就越小。

例如,拨打您的任意最大号码M,选择n以便

2^n >> M /* 2^n is much greater than M */

现在,您的随机数是

M * (rand(2^n) / 2^n)

其中rand是上面第一段中描述的程序。

答案 2 :(得分:0)

随机数生成器创建具有整数位的随机数。如果数字是真正的统计随机,则每个位独立于其他位,您可以使用或丢弃它们的任何组合。对于您的示例,您可以简单地丢弃7位并且具有无偏差的数字。

对于不是2的幂的范围,您可以计算范围的大小并获得每个范围的随机数,然后将它们组合。如果我们假设函数randint(n)0n-1之间提供无偏的随机数,则通用公式为:

(((randint(A) * B + randint(B)) * C + randint(C)) * D + randint(D)) ...

例如,如果您的范围为0-10^616-1,则可以将其计入5^616*2^616

rand_10_616 = randint(5^616) * 2^616 + randint(2^616)

显然,你仍然无法获得5^616的无偏结果,但这是一个较小的问题需要解决。