Generation of (pseudo) random constrained values of (U)Int64 and Decimal
注:为了简洁起见,下面将不区分随机性和伪随机性。此外,在这种情况下,约束意味着在给定的最小值和最大值之间)
考虑剩下的基本数据类型(不包括对象):decimal和(u)int64。他们的随机生成也被处理了(十进制,(u)int64使用
简而言之,我要求正确执行以下功能:
1 2 | long NextLong(long min, long max) long NextDecimal(decimal min, decimal max) |
注意,由于
这应该可以做到。对于decimal,我使用jon skeet的初始方法生成随机
注意,对于
对于
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | 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; } } |
假设您知道如何生成n个随机位。使用
要在正确的范围内生成long/ulong,请计算范围有多大,以及表示该范围需要多少位。然后,您可以使用拒绝采样,这将最坏地拒绝生成值的一半(例如,如果您想要一个在范围[0,128]内的值,这意味着您将多次生成[0,255]。如果您想要一个非零的范围,只需计算出范围的大小,在[0,size]中生成一个随机值,然后添加基数。
生成一个随机的小数是非常困难的,我相信-除了其他的,你必须指定你想要的分布。
我来这里寻找一种在任意范围内生成64位值的方法。其他答案在给定范围(例如long.minvalue到long.maxvalue)时无法生成随机数。这是我的版本,似乎可以解决问题:
1 2 3 4 5 6 7 8 9 | public static long NextInt64(this Random random, long minValue, long maxValue) { Contract.Requires(random != null); Contract.Requires(minValue <= maxValue); Contract.Ensures(Contract.Result<long>() >= minValue && Contract.Result<long>() < maxValue); return (long)(minValue + (random.NextUInt64() % ((decimal)maxValue - minValue))); } |
它使用以下扩展方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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<byte[]>() != null && Contract.Result<byte[]>().Length == byteCount); var buffer = new byte[byteCount]; random.NextBytes(buffer); return buffer; } |
即使请求范围的大小不是2^64的干净除数,分布也不完美,但它至少为任何给定范围提供了请求范围内的随机数。
根据乔恩·斯基特的方法,我想说的是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | 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; } |
如果发现任何错误,请通知我。
1 2 3 4 | long posNum = BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 0); use this instead of NextBytes |