【问题标题】:Random number in range with equal probability等概率范围内的随机数
【发布时间】:2012-04-16 17:32:55
【问题描述】:

这可能比 C# 更与数学相关,但我需要一个 C# 解决方案,所以我把它放在这里。

我的问题是关于随机数生成器的概率,更具体地说,是否每个可能的值都以相等的概率返回。

我知道有 Random.Next(int, int) 方法返回第一个整数和最后一个整数之间的一个数字(最后一个是排他的)。

Random.Next() [无重载] 将返回一个介于 0 和 Int32.MaxValue(即 2147483647)- 1 之间的值,因此为 2147483646。

如果我想要一个介于 1 和 10 之间的值,我可以调用 Random.Next(1, 11) 来执行此操作,但是 1 和 10 之间的每个值都有相同的发生概率吗?

例如,范围是 10,所以 2147483646 不能完全被 10 整除,所以值 1-6 出现的概率稍高(因为2147483646 % 10 = 6)。这当然是假设Random.Next() [没有重载] 中的每个值都以相等的概率返回 0 到 2147483646 之间的值。

如何确保一个范围内的每个数字都有相同的出现概率?假设对于一个彩票类型的系统,某些人比其他人拥有更高的概率是不公平的,我并不是说我会为此使用 RNG 中内置的 C#,我只是将其用作示例。

【问题讨论】:

  • Random 不是要使用均匀数字分布的对象,因为返回的值必然是随机的(或在这种情况下是伪随机的)。使用随机数生成器完全可以生成 1,1,1,1,1,1 的序列。事实上,每次调用它时,它都可能始终为 1,尽管这不太可能。
  • 但从长远来看,它会(应该)限制为均匀随机分布。
  • @Tejs:掷硬币 100 次,得到 100 次同一张脸也是可能的(但不太可能)
  • 从长远来看是均匀分布的。如果您想要更强大的东西,请查看 Mersenne Twister 或加密 PRNG。
  • @Matthew:这就是均匀分布。

标签: c# random range prng


【解决方案1】:

我注意到没有人真正回答你帖子中的肉质问题:

例如,范围是 10,所以 2147483646 不能完全被 10 整除,所以值 1-6 出现的概率略高(因为 2147483646 % 10 = 6)。这当然是假设 Random.Next() [没有重载] 中的每个值都以相等的概率返回 0 到 2147483646 之间的值。

如何确保一个范围内的每个数字都有相同的出现概率?

是的,所以你只需丢弃导致不平衡的值。例如,假设您有一个可以在{ 0, 1, 2, 3, 4 } 上产生均匀分布的 RNG,并且您想使用它在{ 0, 1 } 上产生均匀分布。天真的实现是:从{0, 1, 2, 3, 4} 中绘制,然后返回值% 2;然而,这显然会产生有偏差的样本。发生这种情况是因为,正如您所注意到的,5(项目数)不能被 2 整除。因此,相反,抛出任何产生值4 的平局。因此,算法将是

 draw from { 0, 1, 2, 3, 4 }
 if the value is 4, throw it out
 otherwise, return the value % 2

你可以用这个基本思想来解决一般问题。

但是,1 到 10 之间的每个值出现的概率相等吗?

是的,确实如此。来自MSDN:

伪随机数从有限的一组数字中以相等的概率选择。

编辑:显然文档与 .NET 中的当前实现不一致。文档说明抽签是统一的,但代码表明它不是。但是,这并不能否定这是一个可以解决的问题,我的方法是解决它的一种方法。

【讨论】:

  • 这是否意味着采用范围的重载 Random.Next 方法会对可能引入偏差的数字执行这种“重新滚动”方案?
  • @Matthew:嗯,我不知道他们的具体实现是什么,但我概述的方法是最常见的方法。
  • 谢谢你,我可能会做一些挖掘工作,看看微软在内部做了什么,但对我来说你说的是有道理的,而且是我能想到的唯一适用于任意范围的东西.
  • @Matthew Next(min, max) 方法不执行“重新滚动”。
  • Random.Next(min, max) 本质上实现为(int) (NextDouble() * (max - min) + min)。顺便说一句,我认为draw from { 0, 1, 2 } 中有一个错字 - 应该也包括 3 和 4?
【解决方案2】:

如您所料,RNG 中内置的 C# 是一个均匀分布的 C#。在您为Next(min, max) 指定的范围内,每个数字出现的可能性都相同。

您可以自己进行测试(我有),例如采集 1M 个样本并存储每个数字实际出现的次数。如果你绘制它,你会得到一条几乎平坦的曲线。

另请注意,每个数字具有相同的可能性并不意味着每个数字将出现相同的次数。如果您正在查看从 1 到 10 的随机数字,在 100迭代,它不会是每个数字出现 10 次的均匀分布。有些数字可能出现 8 次,而另一些数字可能出现 12 或 13 次。然而,随着更多的迭代,这往往会有所平衡。

另外,由于它在 cmets 中提到,我会补充:如果您想要更强大的东西,请查找加密 PRNG。就我所见,Mersenne Twister 特别好(速度快、计算成本低、周期长),并且它在 C# 中具有开源实现。

【讨论】:

  • 我的问题更多的是 PRNG 是否给出 1 到 16(含)之间的值,并且我们假设每个数字具有相等的概率,但我需要 1 和 10 的值,我该如何翻译1-16 到 1-10 的值,其中 1-10 的每个值具有相等的概率。我唯一能想到的是,如果数字超出范围,请重新滚动直到超出范围。
  • @Matthew 你可以找到 16 和 10 的最小公倍数(即 80)。这意味着您可以选择 1-16 之间的 5 个随机数,将它们相加,然后除以 8(并对结果进行下限)。您现在有一个介于 1 和 10 之间的随机数,每个数字的概率相等。
  • 编辑,我的最后一篇文章,这会使结果偏向中间。
  • @Servy 这是解决问题的有趣方法,谢谢。
  • @ashes999:“更强大的东西”是什么意思?并且在 MT 的文档中强调它不是用于密码学的,那么你为什么要像它一样提到它呢? MT 基于 624 个状态(查看 C 代码,您会看到“static unsigned long mt[624];”(它是状态向量)。一旦您知道 MT 的 624 个输出,您就可以预测整个系列。这强调不适合密码学。所以,我不得不问,你的意思是什么?你在答案和 cmets 中的陈述表明你根本不了解随机性。
【解决方案3】:

测试程序:

var a = new int[10];
var r = new Random();
for (int i = 0; i < 1000000; i++) a[r.Next(1, 11) - 1]++;
for (int i = 0; i < a.Length; i++) Console.WriteLine("{0,2}{1,10}", i + 1, a[i]);

输出:

1 99924 2 100199 3 100568 4 100406 5 100114 6 99418 7 99759 8 99573 9 100121 10 99918

结论:

每个值都以相等的概率返回。

【讨论】:

  • 加个%值就说明每个值的差别真的很小
  • 虽然差别很小,但并不相等,所以你的结论是错误的。
  • @ashes999 只要种子足够“随机”,就不是真的。任何给定值的概率都是相等的。您只能得到一个值,并且每个值的概率仍然相同(提供的范围为 10%)。
  • @AshBurlaczenko 需要进行卡方分析,以确定结果与每个预期输出(100,000)的比较情况,并根据结果(可能)统计断言函数反映均匀分布。
  • @ashes999:为什么对于较少的迭代次数不正确?等概率并不意味着如果您迭代 10 次,每个数字都会出现一次。
【解决方案4】:

Ashes 和 dtb 不正确:您怀疑某些数字比其他数字更有可能发生是正确的。

当您调用.Next(x, y) 时,有y - x 个可能的返回值。 .NET 4.0 Random 类根据NextDouble() 的返回值计算返回值(这是一个稍微简化的描述)。

显然,可能的 double 值集是有限的,而且,正如您所注意到的,它可能不是 .Next(x, y) 的可能返回值集的大小的倍数。因此,假设输入值集合是均匀分布的,那么某些输出值出现的概率会稍大。

我不知道有多少数字双精度值(即,不包括无穷大和 NaN 值),但它肯定大于 2^32。在您的情况下,如果我们假设 2^32 个值,那么我们必须将 4294967296 个输入映射到 10 个输出。某些值的发生概率会大 429496730 / 429496729,或者大 0.00000023283064397913028110629%。事实上,由于输入状态的数量大于 2^32,所以概率差异会更小。

【讨论】:

  • 这正是我所关心的。不过,@Jason 给我的答案似乎解决了我的问题。
  • @phoog:你可以丢弃导致偏差的值。
  • @Jason 但你不能用Next(min, max) 做到这一点,因为你不控制它的实现。要使用System.Random 执行此操作,您需要知道来自NextDouble() 的可能返回值集,或者,您可以创建一个派生类并提供您自己的Sample() 实现(由NextDouble())。
  • @Jason 当然。我注意到您在文档中的引用是关于 Random 类页面中随机数的一般性陈述,而不是关于 Next(min, max) 方法行为的陈述。整数 Next 方法的页面没有提及概率,无论是哪种方式。
  • @Jason 没有;相反,我建议不要信仰飞跃。这就是我反编译 IL 的原因。
猜你喜欢
  • 1970-01-01
  • 2011-08-26
  • 2013-05-02
  • 1970-01-01
  • 1970-01-01
  • 2019-05-18
  • 2011-04-10
  • 1970-01-01
  • 2011-12-16
相关资源
最近更新 更多