代码之家  ›  专栏  ›  技术社区  ›  Ohad Schneider

(u)int64和decimal的(伪)随机约束值的生成

  •  5
  • Ohad Schneider  · 技术社区  · 16 年前

    注: 为了简洁起见,下面将不区分随机性和伪随机性。此外,在这种情况下, 约束的 方法 在给定的最小值和最大值之间 )

    这个 System.Random 类提供整数、双精度数组和字节数组的随机生成。 接下来,可以轻松地生成Boolean、Char、Byte、Int16、Int32类型的随机约束值。使用 Random.NextDouble() 同样,可以生成double和single类型的约束值(就我对该类型的理解而言)。随机字符串生成(给定长度和字母表) has also been tackled before .

    考虑剩余的基本数据类型(不包括对象):decimal和(u)int64。他们的随机产生也被解决了( Decimal , (U)Int64 使用 Random.NextBytes() ,但在受到约束时不会。理论上可以使用拒绝抽样(即循环直到生成的值是所需的范围),但这显然不是一个实际的解决方案。正火 NextDouble() 因为没有足够的有效数字,所以无法工作。

    简而言之,我要求正确执行以下功能:

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

    注意,因为 System.DateTime 基于一个ulong,第一个函数将允许随机约束生成这样的结构(类似于 here ,仅以滴答而不是分钟为单位)。

    5 回复  |  直到 8 年前
        1
  •  6
  •   Jon Skeet    16 年前

    假设您知道如何生成n个随机位。这两种方法都很容易做到 NextBytes 或重复呼叫 Random.Next 有适当的限制。

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

    生成一个随机的小数是非常困难的,我相信-除了其他的,你必须指定你想要的分布。

        2
  •  9
  •   jason    16 年前

    应该这样做。对于decimal,我使用jon skeet的初始方法生成随机的 decimal S(无约束)。为了 long 我提供了一种产生随机非负的方法 长的 s,用于在随机范围内创建a值。

    注意这是为了 十进制的 结果分布不是 [minValue, maxValue] . 它只是在范围内所有小数的位表示上是一致的。 [最小值,最大值] . 如果不使用拒绝抽样,我看不到一个简单的解决方法。

    为了 长的 结果分布在 [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;
        }
    }
    
        3
  •  0
  •   Allon Guralnek    14 年前

    我来这里寻找一种在任意范围内生成64位值的方法。其他答案在给定范围(例如long.minvalue到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<long>() >= minValue &&
                         Contract.Result<long>() < 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<byte[]>() != null &&
                         Contract.Result<byte[]>().Length == byteCount);
    
        var buffer = new byte[byteCount];
        random.NextBytes(buffer);
        return buffer;
    }
    

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

        4
  •  0
  •   Hille    8 年前

    根据乔恩·斯基特的方法,我想说的是:

    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;
    }
    

    如果发现任何错误,请通知我。

        5
  •  -2
  •   zing ming    15 年前
    long posNum = BitConverter.ToInt64(Guid.NewGuid().ToByteArray(), 0); 
    
    
     use this instead of NextBytes
    
    推荐文章