【问题标题】:Random generation with very low chances机会非常低的随机生成
【发布时间】:2014-05-07 15:52:43
【问题描述】:

我正在尝试在 C# 中为科学项目获取随机布尔值。问题是 bool 为真的机会必须在 10^-12 到 10^-14 左右;默认随机函数仅生成整数,并且 0 和 [u]int.max 之间的值非常小。

如何生成一个概率如此低的随机布尔值?

【问题讨论】:

  • 使用NextBytes可以获得更大的取值范围

标签: c# random


【解决方案1】:

编辑

Random.NextDouble() 不会给出预期的结果!

正如Kyle 在下面的 cmets 中所建议的那样,Random.NextDouble() 的实现将int 映射到区间 [0.0 , 1.0),从而有效地使我的代码等效于 r.Next( 0, Int32.MaxValue ) == 0。添加更多的零不会影响结果为假的概率。

使用其他答案之一,或使用此代码从 System.Random 生成 ulong(范围 0-18446744073709551615)。

var r = new Random();
var bytes = new byte[8];
r.NextBytes(bytes);
ulong result = BitConverter.ToUInt64(bytes, 0);

原答案

警告:不会按预期工作,请勿使用!仅供参考。


改用Random.NextDouble():结果是0.0和1.0之间的双精度数

var r = new Random();
var probablyFalseBool = r.NextDouble() < 0.0000000000001;

如有必要,改变零的数量。

【讨论】:

  • 接受了这个答案,因为它是最短的方法。你会在来源部分被提及,谢谢;)
  • 我认为这不会如您所愿。基于 .NET 4.5 中 System.Random 的 source code,此处给定的代码将产生与 var probablyFalseBool = r.Next( 0, Int32.MaxValue ) == 0 相同的结果,因为 Random.Next 调用与 Random.NextDouble 调用相同的受保护的 Sample 方法。
  • 我同意@Kyle 的观点,NextDouble 方法的分辨率为1/Int32.MaxValue(~1 / 20 亿),因此不足以用于您需要的分辨率。
  • @Kyle 不错!我编辑了我的答案,以引导未来的用户不要为此目的使用 NextDouble()
  • @Rik 感谢测试 + 编辑,现在无法检查 :)
【解决方案2】:

尝试组合多个随机选择:

if(rnd.Next(0,10000) == 0 && rnd.Next(0,10000) == 0 && rnd.Next(0,10000) == 0){
}

不确定 C# 语法是否正确,但这进入 if 块的概率为 10^-12。

【讨论】:

  • 领先我 12 秒。
  • 我相信 Rik 的回答是最简单和更优化的方法。多次组合这个解决方案不是很漂亮,也不是很优化。
  • 没有理由这样做,您可以使用NextBytes获取更大的样本
  • @PanagiotisKanavos,然后根据以十为底的概率提取值变得“有点”复杂。
  • @NathanA BTW,除非 OP 拥有某种超级计算机,否则我怀疑他会以 10^-14 的概率看到任何结果。
【解决方案3】:

您可以使用NextBytes 方法获取大随机数。

例如,这将为您提供 2.842e-14 的概率,即布尔值为 true

Random r = new Random();
byte[] buffer = new byte[6]; // make an array with 48 bits
r.NextBytes(buffer);
buffer[0] &= 0xf8; // mask out three bits to make it 45
bool occured = buffer.Sum(b => b) == 0;

【讨论】:

    【解决方案4】:

    正如 Nathan 所建议的,您可以使用 Next 方法来指定结果范围。

    然后您可以使用&amp;&amp; 运算符来确保它符合您的精度。

    // 1 in a million
    var result = random.Next(0, 1000) == 0 && random.Next(0, 1000) == 0; 
    

    然后您可以更改它们以提供您想要的价格。

    【讨论】:

      【解决方案5】:

      您可以像这样轻松控制赔率:

      var result = rnd.Next(0,100) == 0;
      

      这将使result 的几率为 100 分之一。您可以轻松地将值调整为您想要的任何几率。

      【讨论】:

      • 我不认为Next 方法本身具有适当的精度。
      • 这似乎不符合 1/10,000,000,000,000 的赔率。
      • 他的要求明确说明了为什么他不能这样做
      • @paqogomez 100 是排他性的,所以取值为 0-99,意思是 100 次变化中的 1 次。
      • @NathanA 人们在说Next() 方法不包含必要的精度 - 参数类型不够大,无法容纳值 10,000,000,000,000。
      【解决方案6】:

      去多维!一个正常的随机数是一维的(即沿着一条线)。通过将两个随机数相乘,您可以使用二维等。

      如果你取 6 个 1 到 100 之间的随机整数,然后将它们相乘,得到结果 1 的几率是 10^12 中的 1。与 1 到 1000 之间的 4 个随机整数或 1 到 10000 之间的 3 个随机整数相同。

      如果您想要 10^12 中的 30,则使用 1 到 1,000,000 之间的两个随机数,并且您希望一个为 1,另一个为 30 或更少。

      如果您想要 10^12 中的 x (

      【讨论】:

        【解决方案7】:

        您可以使用Round.NextDoubleRound.NextBytes 而不是Round.Next 以获得更广泛的范围。

        使用NextBytes,您可以使用 4-5 字节的缓冲区来获得所需的范围。非常粗略,你可以这样做:

        var buffer=new byte[5];
        rnd.NextBytes(buffer);
        return buffer.All(b=>b>0);
        

        您可能可以通过使用BitArray 来加快速度

        使用NextDouble,您需要检查结果是否小于 1/10^12。

        【讨论】:

          【解决方案8】:

          在一行中生成两个整数表示

          (target_value div int_max, target_value mod int_max)
          

          检查较大的为0,第二个为小于desired_precison * int_max

          如果您对引入偏差有疑问,请在测试前随机化该对中两个整数的顺序。

          此独立代码说明了该技术(已测试here):

          using System.IO;
          using System;
          
          class Program
          {
              static void Main(string[] args)
              {
                  bool   flip, strike;
                  int    eps, lo, hi;
                  Random rnd;
          
                  eps = Int32.Parse(args[0]); // whatever is appropriate
          
                  rnd = new Random();
                  lo = rnd.Next(Int32.MaxValue);
                  hi = rnd.Next(Int32.MaxValue);
                  flip = (rnd.Next() > 0.5); 
                  if (flip) { int swap; swap = lo; lo = hi; hi = swap; }
          
                  strike = (hi == 0) && (lo < eps);
          
                  Console.Write("[hi lo] = ");
                  Console.Write(hi);
                  Console.WriteLine(lo);
                  if (strike) {
                      Console.WriteLine("strike");
                  }
              }
          }
          

          【讨论】:

          • 好主意,但它的性能很差。执行 10 亿次需要 38807 毫秒,几乎是标记方法(19630 毫秒)的两倍。
          • 是的,对 prng 的调用应该支配执行时间。
          • 不,必须在其他地方,因为 4 倍随机函数仍然快两倍。我删除了控制台输出和变量声明,仍然没有更好。测试的src是here
          • 在您的测试代码中,建议的方法以大约 1 / 2^-(31+31-4) ~ 3.5*10^-18 的概率触发;因此,您的目标阈值以下大约 2 个数量级!
          • 是的,但这只是测试代码。我将在具有动态机会计算的大框架中使用它,实际代码是 chance = 2.5 * Math.Pow(10, -12) * (((tdx21+tdx12)/2)*(30/3.6)) + ((2949768/(5.1*(Math.Pow(10, -14))))*(157766400^-1)) * (((tdx21+tdx12)/2)*(30/3.6)); ;不适合基准。
          猜你喜欢
          • 2011-07-23
          • 1970-01-01
          • 2014-09-03
          • 2017-09-07
          • 1970-01-01
          • 2011-07-03
          • 2016-06-10
          • 1970-01-01
          • 2020-05-22
          相关资源
          最近更新 更多