Random() 乘以 2^32 - 1 始终返回偶数最后一位数字

我遇到过这种奇怪的行为,这可能最好用一个小例子来描述:


Random R = new Random();


for (int i = 0; i < 10_000; i++)

{

    double d = R.NextDouble() * uint.MaxValue;

}

d现在,小数点前的最后一位数字始终是偶数,即int r = (int) (d % 10)始终是 0、2、4、6 或 8。不过,两边都有奇数位。


我怀疑与uint.MaxValue(2^32 - 1) 相乘可能会在最后一位数字中强制出现一些舍入误差,但由于 double 具有超过 50 位的精度,因此这应该会击败 uint,在分隔符后保留大约 20 位。uint.MaxValue如果我在乘法之前显式存储为双精度值并使用它,也会发生此行为。


有人可以阐明这一点吗?


侃侃无极
浏览 101回答 1
1回答

BIG阳

这是.Net Random 类的缺陷。如果您检查源代码,您将在私有方法的实现中看到以下注释GetSampleForLargeRange():      // The distribution of double value returned by Sample       // is not distributed well enough for a large range.      // If we use Sample for a range [Int32.MinValue..Int32.MaxValue)      // We will end up getting even numbers only.这在 Next() 的实现中使用:public virtual int Next(int minValue, int maxValue) {  if (minValue>maxValue) {      throw new ArgumentOutOfRangeException("minValue",Environment.GetResourceString("Argument_MinMaxValue", "minValue", "maxValue"));  }  Contract.EndContractBlock();  long range = (long)maxValue-minValue;  if( range <= (long)Int32.MaxValue) {        return ((int)(Sample() * range) + minValue);  }            else {       return (int)((long)(GetSampleForLargeRange() * range) + minValue);  }}但它不用于从返回的值NextDouble()(它只返回从Sample().所以答案是NextDouble()分布不均匀。您可以使用它RNGCryptoServiceProvider来生成更好的随机数,但创建双精度数有点麻烦。static void Main(){    var R = new RNGCryptoServiceProvider();    var bytes = new Byte[8];    for (int i = 0; i < 10_000; i++)    {        R.GetBytes(bytes);        var ul = BitConverter.ToUInt64(bytes, 0) / (1 << 11);        var d  = ul / (double)(1UL << 53);        d *= uint.MaxValue;        Console.WriteLine(d);    }}
打开App,查看更多内容
随时随地看视频慕课网APP