将伪随机数约束到较小范围的正确方法是什么?

时间:2009-04-09 14:28:50

标签: language-agnostic random

将PRNG值限制在较小范围内的最佳方法是什么?如果您使用模数,旧的最大数字不能被新的最大数字整除,那么您偏向0(old_max - new_max - 1)。我认为最好的方法是这样的(这是浮点数,而不是整数数学)

random_num = PRNG() / max_orginal_range * max_smaller_range

但是我的直觉中有些东西让我质疑那种方法(可能是浮点实现和表示差异?)。

随机数生成器将在硬件和软件平台上产生一致的结果,并且约束也需要。

我有理由怀疑上面的伪代码(但不是出于我的想法)。 MichaelGG的answer让我以不同的方式思考这个问题。我可以使用较小的数字对其进行建模并测试每个结果。所以,我们假设我们有一个PRNG产生0到31之间的随机数,你希望较小的范围是0到9.如果你使用模数你偏向0,1,2和3.如果你使用伪代码在你偏向0,2,5和7之上。我认为没有一种方法可以将一组映射到另一组。到目前为止,我提出的最好的方法是重新生成大于old_max/new_max的随机数,但这也存在深层问题(减少生成新数字的时间,时间,直到一个数据在正确的范围内)等)。

我想我可能会天真地接近这个问题。可能是时候开始对文献进行一些认真的研究了(有人必须先解决这个问题)。

6 个答案:

答案 0 :(得分:2)

我知道这可能不是一个特别有用的答案,但我认为最好的方法是设想一些不同的方法,然后尝试几百万次,并检查结果集。

如有疑问,请亲自尝试。

修改

应该注意的是,许多语言(如C#)内置了功能限制

int maximumvalue = 20;
Random rand = new Random();

rand.Next(maximumvalue);

只要有可能,你应该使用那些而不是你自己编写的任何代码。不要重新发明轮子。

答案 1 :(得分:1)

这个问题类似于只给一个 p 边的骰子滚动一个 k 边的骰子,而不浪费随机性。

从这个意义上说,B。Kloeckner在“ Simulating a dice with a dice”中的引理3中,除非“除以 k 的每个素数也除以 p ”。因此,例如,如果 p 是2的幂(并且任何随机位块都与以2个数量的面的幂滚动骰子相同)和 k 的主要因子不是2,最好的办法是任意获取,几乎不浪费随机性,例如,将多卷 p 双面模具分批放入,直到 p ^ n 足够接近于 k 的幂。

让我也谈谈您对重新生成随机数的一些担忧:

  • “缩短周期”:除了批量处理比特位之外,还可以通过以下几种方式解决此问题:
  • “生成新数字直到一个在正确范围内的时间”:如果您想要无偏随机数,那么任何这样做的算法通常都将在最坏的情况下永远运行。同样,在引理3中,算法将在最坏的情况下永远运行,除非“每个素数除以 k 也除以 p ”,如果不是这样,情况就不是这样了。 k 是10, p 是32。

另请参阅问题:How to generate a random integer in the range [0,n] from a stream of random bits without wasting bits?,尤其是我在那的答案。

答案 2 :(得分:0)

如果PRNG()生成均匀分布的随机数,那么上面看起来很好。事实上(如果你想缩放平均值等),上述应该适用于所有目的。我想你需要问一下与原始PRNG()相关的错误是什么,以及进一步的操作是否会大幅增加。

如果有疑问,请生成适当大小的样本集,并在Excel或类似内容中查看结果(检查您的平均值/ std.dev等等,以了解您的期望)

答案 3 :(得分:0)

如果您有权访问PRNG函数(例如,random()),那么将生成范围为0< = x< 0的数字。 1,你能不能做到:

 random_num = (int) (random() * max_range);

给你0到max_range范围内的数字?

答案 4 :(得分:0)

以下是CLR的Random类在受限时的工作方式(根据Reflector):

long num = maxValue - minValue;
if (num <= 0x7fffffffL) {
    return (((int) (this.Sample() * num)) + minValue);
}
return (((int) ((long) (this.GetSampleForLargeRange() * num))) + minValue);

即使给你一个积极的int,也不难让它达到双倍。只需将随机int乘以(1 / maxint)即可。从32位int到double应该提供足够的精度。 (我实际上没有像这样测试PRNG,所以我可能会错过浮点数。)

答案 5 :(得分:0)

Psuedo随机数生成器基本上产生1和0的随机序列,当它们相互附加时,在基数2中是无限大的数。每次你从你的prng中消耗一点时,你将这个数除以2并保持模数。你可以永远这样做,而不会浪费一点。

如果你需要一个[0,N]范围内的数字,那么你需要相同的,但你需要基数为N而不是基数为2.转换基数基本上是微不足道的。使用您需要的位数,将这些位的其余部分返回到您的prng,以便在下次需要数字时使用。