为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

C#
侃侃无极 2023-08-13 09:40:56
我遇到过这种奇怪的行为,这可能最好用一个小例子来描述: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如果我在乘法之前显式存储为双精度值并使用它,也会发生此行为。有人可以阐明这一点吗?
查看完整描述

1 回答

?
BIG阳

TA贡献1859条经验 获得超6个赞

这是.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);

    }

}


查看完整回答
反对 回复 2023-08-13
  • 1 回答
  • 0 关注
  • 100 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信