【发布时间】:2014-05-07 15:52:43
【问题描述】:
我正在尝试在 C# 中为科学项目获取随机布尔值。问题是 bool 为真的机会必须在 10^-12 到 10^-14 左右;默认随机函数仅生成整数,并且 0 和 [u]int.max 之间的值非常小。
如何生成一个概率如此低的随机布尔值?
【问题讨论】:
-
使用
NextBytes可以获得更大的取值范围
我正在尝试在 C# 中为科学项目获取随机布尔值。问题是 bool 为真的机会必须在 10^-12 到 10^-14 左右;默认随机函数仅生成整数,并且 0 和 [u]int.max 之间的值非常小。
如何生成一个概率如此低的随机布尔值?
【问题讨论】:
NextBytes可以获得更大的取值范围
编辑
正如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;
如有必要,改变零的数量。
【讨论】:
var probablyFalseBool = r.Next( 0, Int32.MaxValue ) == 0 相同的结果,因为 Random.Next 调用与 Random.NextDouble 调用相同的受保护的 Sample 方法。
NextDouble 方法的分辨率为1/Int32.MaxValue(~1 / 20 亿),因此不足以用于您需要的分辨率。
NextDouble()。
尝试组合多个随机选择:
if(rnd.Next(0,10000) == 0 && rnd.Next(0,10000) == 0 && rnd.Next(0,10000) == 0){
}
不确定 C# 语法是否正确,但这进入 if 块的概率为 10^-12。
【讨论】:
NextBytes获取更大的样本
您可以使用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;
【讨论】:
正如 Nathan 所建议的,您可以使用 Next 方法来指定结果范围。
然后您可以使用&& 运算符来确保它符合您的精度。
// 1 in a million
var result = random.Next(0, 1000) == 0 && random.Next(0, 1000) == 0;
然后您可以更改它们以提供您想要的价格。
【讨论】:
您可以像这样轻松控制赔率:
var result = rnd.Next(0,100) == 0;
这将使result 的几率为 100 分之一。您可以轻松地将值调整为您想要的任何几率。
【讨论】:
Next 方法本身具有适当的精度。
Next() 方法不包含必要的精度 - 参数类型不够大,无法容纳值 10,000,000,000,000。
去多维!一个正常的随机数是一维的(即沿着一条线)。通过将两个随机数相乘,您可以使用二维等。
如果你取 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 (
【讨论】:
您可以使用Round.NextDouble 或Round.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。
【讨论】:
在一行中生成两个整数表示
(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");
}
}
}
【讨论】:
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)); ;不适合基准。