生成(U)Int64和Decimal的(伪)随机约束值

注意:为简洁起见,以下内容无法区分随机性和伪随机性。 此外,在此上下文中, 约束意味着给定的最小值和最大值之间

System.Random类提供整数,双精度和字节数组的随机生成。 使用Random.Next,可以很容易地生成布尔,Char,(S)字节,(U)Int16,(U)Int32类型的随机约束值。 使用Random.NextDouble() ,可以类似地生成Double和Single类型的约束值(就我对这种类型的理解而言)。 随后的字符串生成(给定长度和字母表) 也 已被 解决 。

考虑剩余的原始数据类型(不包括Object):Decimal和(U)Int64。 它们的随机生成也已被解决( Decimal , (U)Int64使用Random.NextBytes() ),但Random.NextBytes() 。 理论上可以使用拒绝采样(即循环直到生成的值是所需范围),但这显然不是一个实际的解决方案。 规范化NextDouble()将无效,因为它没有足够的有效数字。

简而言之,我要求正确实现以下function:

 long NextLong(long min, long max) long NextDecimal(decimal min, decimal max) 

请注意,由于System.DateTime基于ulong,因此第一个函数也允许随机约束生成此类结构(类似于此处 ,仅在刻度而不是分钟内)。

让我们假设您知道如何生成N个随机位。 使用NextBytes或重复调用具有适当限制的Random.Next非常容易。

要在正确的范围内生成long / ulong,请计算出范围的大小以及表示它的位数。 然后,您可以使用拒绝采样,这将最终拒绝生成值的一半(例如,如果您想要一个[0,128]范围内的值,这意味着您将多次生成[0,255])。 如果你想要一个非零的范围,只需计算范围的大小,生成[0,大小)的随机值,然后添加基数。

生成一个随机小数显然更难,我相信 – 除了其他任何东西,你必须指定你想要的分布。

这应该做到这一点。 对于十进制,我使用Jon Skeet的初始方法来生成随机decimal (无约束)。 long我提供了一种生成随机非负long s的方法,然后用它来创建随机范围内的a值。

请注意,对于decimal ,结果分布不是[minValue, maxValue]上的均匀分布。 它只是在小数的所有位表示中均匀,落在[minValue, maxValue]范围内。 没有使用拒绝抽样,我没有看到一个简单的方法。

long ,在[minValue, maxValue)上得到的分布是均匀的。

 static class RandomExtensions { static int NextInt32(this Random rg) { unchecked { int firstBits = rg.Next(0, 1 << 4) << 28; int lastBits = rg.Next(0, 1 << 28); return firstBits | lastBits; } } public static decimal NextDecimal(this Random rg) { bool sign = rg.Next(2) == 1; return rg.NextDecimal(sign); } static decimal NextDecimal(this Random rg, bool sign) { byte scale = (byte)rg.Next(29); return new decimal(rg.NextInt32(), rg.NextInt32(), rg.NextInt32(), sign, scale); } static decimal NextNonNegativeDecimal(this Random rg) { return rg.NextDecimal(false); } public static decimal NextDecimal(this Random rg, decimal maxValue) { return (rg.NextNonNegativeDecimal() / Decimal.MaxValue) * maxValue; ; } public static decimal NextDecimal(this Random rg, decimal minValue, decimal maxValue) { if (minValue >= maxValue) { throw new InvalidOperationException(); } decimal range = maxValue - minValue; return rg.NextDecimal(range) + minValue; } static long NextNonNegativeLong(this Random rg) { byte[] bytes = new byte[sizeof(long)]; rg.NextBytes(bytes); // strip out the sign bit bytes[7] = (byte)(bytes[7] & 0x7f); return BitConverter.ToInt64(bytes, 0); } public static long NextLong(this Random rg, long maxValue) { return (long)((rg.NextNonNegativeLong() / (double)Int64.MaxValue) * maxValue); } public static long NextLong(this Random rg, long minValue, long maxValue) { if (minValue >= maxValue) { throw new InvalidOperationException(); } long range = maxValue - minValue; return rg.NextLong(range) + minValue; } } 

我来到这里寻找一种在任意范围内生成64位值的方法。 当给定特定范围时,其他答案未能产生随机数(例如,long.MinValue to long.MaxValue)。 这是我的版本似乎解决了这个问题:

 public static long NextInt64(this Random random, long minValue, long maxValue) { Contract.Requires(random != null); Contract.Requires(minValue <= maxValue); Contract.Ensures(Contract.Result() >= minValue && Contract.Result() < maxValue); return (long)(minValue + (random.NextUInt64() % ((decimal)maxValue - minValue))); } 

它使用以下扩展方法:

 public static ulong NextUInt64(this Random random) { Contract.Requires(random != null); return BitConverter.ToUInt64(random.NextBytes(8), 0); } public static byte[] NextBytes(this Random random, int byteCount) { Contract.Requires(random != null); Contract.Requires(byteCount > 0); Contract.Ensures(Contract.Result() != null && Contract.Result().Length == byteCount); var buffer = new byte[byteCount]; random.NextBytes(buffer); return buffer; } 

即使请求范围的大小不是2 ^ 64的干净除数,分布也不完全,但它至少在任何给定范围的请求范围内提供随机数。

基于Jon Skeet的方法,这是我的一个方面:

 public static long NextLong(this Random rnd, long min, long max) { if (max <= min) { throw new Exception("Min must be less than max."); } long dif = max - min; var bytes = new byte[8]; rnd.NextBytes(bytes); bytes[7] &= 0x7f; //strip sign bit long posNum = BitConverter.ToInt64(bytes, 0); while (posNum > dif) { posNum >>= 1; } return min + posNum; } 

如果您发现任何错误,请告诉我。

 long posNum = BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 0); use this instead of NextBytes