.net Random.NextDouble恰好为0的几率有多大?

5m1hhzi4  于 2022-12-24  发布在  .NET
关注(0)|答案(3)|浏览(226)

bounty将在5天后过期。回答此问题可获得+50的声誉奖励。ndsvw正在寻找此问题的更详细的答案

Random.NextDouble()的文档:
返回大于或等于0.0且小于1.0的随机浮点数。
所以,它可以正好是0,但这个概率有多大呢?

var random = new Random();
var x = random.NextDouble()
if(x == 0){
    // probability for this?
}

计算Random.Next()为0的概率很容易,但我不知道在这种情况下如何计算......

rhfm7lfc

rhfm7lfc1#

正如在注解中提到的,它依赖于NextDouble的内部实现。在“旧的”.NET框架中,以及在最高版本5的现代.NET中,它看起来像这样:

protected virtual double Sample() {
    return (InternalSample()*(1.0/MBIG));
}

InternalSample返回0到Int32.MaxValue范围内的整数,包括0,不包括int.MaxValue。我们可以假设InternalSample的分布是均匀的(在用于Nextdocs方法中,该方法只调用InternalSample,有线索表明它是,似乎没有理由在通用的整数RNG中使用非均匀分布)。这意味着每个数字的概率都相等。那么,我们有2,147,483,647个数字处于分布中,抽到0的概率是1 /2,147,483,647。
在现代的.NET 6+中有两种实现。第一种是在您向Random构造函数提供显式种子值时使用的。这种实现与上面的相同,并且是出于兼容性原因而使用的-以便依赖种子值来产生确定性结果的旧代码在移动到新的.NET版本时不会中断。
第二个实现是一个新的实现,当您不将seed传递到Random构造函数时使用。源代码:

public override double NextDouble() =>
    // As described in http://prng.di.unimi.it/:
    // "A standard double (64-bit) floating-point number in IEEE floating point format has 52 bits of significand,
    //  plus an implicit bit at the left of the significand. Thus, the representation can actually store numbers with
    //  53 significant binary digits. Because of this fact, in C99 a 64-bit unsigned integer x should be converted to
    //  a 64-bit double using the expression
    //  (x >> 11) * 0x1.0p-53"
    (NextUInt64() >> 11) * (1.0 / (1ul << 53));

我们首先获得随机的64位无符号整数,现在,我们可以将其乘以1 / 2^64以获得0..1范围内的double,但这会使结果分布有偏差。double由53位尾数表示(52位是显式的,1位是隐式的)、指数和符号。对于所有整数值,指数是相同的,所以我们只剩下53位来表示整数值,但这里有64位整数,这意味着小于2^53的整数值可以用double来表示,但更大的整数不能,例如:

ulong l1 = 1ul << 53;
ulong l2 = l1 + 1;
double d1 = l1;
double d2 = l2;
Console.WriteLine(d1 == d2);

打印“true”,两个不同的整数Map到相同的double值,这意味着如果我们将64位整数乘以12^64,我们将得到一个有偏的非均匀分布,因为许多大于2^53-1的整数将Map到相同的值。
因此,我们丢弃11位,然后将结果乘以1 / 2^53,得到在0..1范围内的均匀分布,得到0的概率是1 / 2^53(1 /9,007,199,254,740,992)。此实现比旧实现更好,因为在0..1范围内,它提供了更多不同的双精度值(2^53,而旧版本中为2^32)。
你还在评论中问:
如果知道0(包括0)和1(不包括1)之间有多少个数字(根据IEEE 754),就有可能回答“概率”问题,因为0是所有数字之一
事实并非如此。在IEEE 754中,在0..1之间实际上有超过2^53个可表示的数字。我们有52位尾数,然后有11位指数,其中一半是负指数。几乎所有负指数(大约是11位范围的一半)与尾数结合在一起,都会给出0..1范围内的不同值。
为什么我们不能使用IEEE允许我们生成随机数的完整的0..1范围?因为这个范围是不均匀的(就像完整的双精度范围本身是不均匀的)。例如,在0..0.5范围内比在0.5..1范围内有更多的可表示数。

bgtovc5b

bgtovc5b2#

这是从一个严格的学术Angular 。
Double Struct开始:
所有浮点数的有效位数也都有限,这也决定了浮点值近似实数的精确度。Double值的精度最高可达15位小数,但内部最多保留17位。这意味着某些浮点运算可能缺少更改浮点值所需的精度。
如果只有15位小数有效,则可能的返回值为:

0.000000000000000

收件人:

0.999999999999999

换句话说,你有10^15个可能的(相对不同的,"不同的")值(参见第一个答案中的Permutations):

10^15 = 1,000,000,000,000,000

零只是这些可能性之一:
以百分比表示:

0.0000000000001% chance of zero being randomly selected?

我想这是你能得到的最接近"正确"的答案...
......它在实践中是否以这种方式执行可能是另一个故事。

14ifxucb

14ifxucb3#

只需要创建一个简单的程序,让它运行,直到你对尝试的次数感到满意为止。(参见:https://onlinegdb.com/ij1M50gRQ

Random r = new Random();
       Double d ;
       int attempts=0;
       int attempts0=0;
       while (true) {
            d = Math.Round(r.NextDouble(),3);
            if(d==0) attempts0++;
            attempts++;
            if (attempts%1000000==0) Console.WriteLine($"Attempts: {attempts}, with {attempts0} times a 0 value, this is {Math.Round(100.0*attempts0/attempts,3)} %");
       }

示例输出:

...
Attempts: 208000000, with 103831 times a 0 value, this is 0.05 %
Attempts: 209000000, with 104315 times a 0 value, this is 0.05 %
Attempts: 210000000, with 104787 times a 0 value, this is 0.05 %
Attempts: 211000000, with 105305 times a 0 value, this is 0.05 %
Attempts: 212000000, with 105853 times a 0 value, this is 0.05 %
Attempts: 213000000, with 106349 times a 0 value, this is 0.05 %
Attempts: 214000000, with 106839 times a 0 value, this is 0.05 %
...

d的值四舍五入为2位小数将返回0.5%

相关问题