c# - int.MinValue和int.MaxValue之间的随机数,包括端点




.net random (10)

这里有点令人费解: Random.Next() 有一个接受最小值和最大值的重载。 此重载返回一个大于或等于最小值(包括)且小于最大值(不包括)的数字。

我想包括整个范围,包括最大值。 在某些情况下,我可以通过添加一个到最大值来实现这一点。 但在这种情况下,最大值可以是 int.MaxValue ,并且向此添加一个将无法实现我想要的。

那么有没有人知道从 int.MinValueint.MaxValue 获取随机数的好方法呢?

更新:

请注意,较低的范围可以是 int.MinValue 但也可以是其他值。 如果我知道它总是 int.MinValue 那么问题会更简单。


你不能使用 Random.Next() 来实现你想要的,因为你不能将 N 数字的序列对应到 N+1 并且不能错过一个:)。 期。

但你可以使用 Random.NextDouble() ,它返回双重结果: 0 <= x < 1 又名 [0, 1) 介于0之间,其中 [ 是包含符号和 ) 独占

我们如何将N个数字与[0,1]对应? 您需要将 [0, 1) 分割为N个相等的段: [0, 1/N)[1/N, 2/N) ,... [N-1/N, 1)

在这里,重要的是一个边界是包容性的而另一个是独占的 - 所有N个部分绝对平等!

这是我的代码: 我把它作为一个简单的控制台程序。

class Program
    {
        private static Int64 _segmentsQty;
        private static double _step;
        private static Random _random = new Random();

        static void Main()
        {
            InclusiveRandomPrep();
            for (int i = 1; i < 20; i++)
            {
                Console.WriteLine(InclusiveRandom());
            }

            Console.ReadLine();
        }

        public static void InclusiveRandomPrep()
        {
            _segmentsQty = (Int64)int.MaxValue - int.MinValue;
            _step = 1.0 / _segmentsQty;
        }
        public static int InclusiveRandom()
        {
            var randomDouble = _random.NextDouble();
            var times = randomDouble / _step;
            var result = (Int64)Math.Floor(times);
            return (int)result + int.MinValue;
        }
    }

你可以试试这个。 有点hacky但可以让你包括最小和最大。

static void Main(string[] args)
        {
            int x = 0;

            var r = new Random();

            for (var i = 0; i < 32; i++)
            {
                x = x | (r.Next(0, 2) << i);

            }

            Console.WriteLine(x);
            Console.ReadKey();
        }

实际上有趣的是,这不是Random.Next(int,int)的实现,因为你可以从包容的行为中导出排他的行为,而不是相反的行为。

public static class RandomExtensions
{
    private const long IntegerRange = (long)int.MaxValue - int.MinValue;

    public static int NextInclusive(this Random random, int minValue, int maxValue)
    {
        if (minValue > maxValue)
        {
            throw new ArgumentOutOfRangeException(nameof(minValue));
        }

        var buffer = new byte[4];
        random.NextBytes(buffer);
        var a = BitConverter.ToInt32(buffer, 0);
        var b = a - (long)int.MinValue;
        var c = b * (1.0 / IntegerRange);
        var d = c * ((long)maxValue - minValue + 1);
        var e = (long)d + minValue;
        return (int)e;
    }
}
new Random(0).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue
new Random(1).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue - 1
new Random(0).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue + 1
new Random(1).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue
new Random(-451732719).NextInclusive(int.MinValue, int.MaxValue); // returns int.MinValue
new Random(-394328071).NextInclusive(int.MinValue, int.MaxValue); // returns int.MaxValue

将范围拆分为两个,并补偿 MaxValue

r.Next(2) == 0 ? r.Next(int.MinValue, 0) : (1 + r.Next(-1, int.MaxValue))

如果我们制作相同大小的范围,我们可以用不同的数学得到相同的结果。 这里我们依赖于 int.MinValue = -1 - int.MaxValue 的事实:

r.Next(int.MinValue, 0) - (r.Next(2) == 0 ? 0 : int.MinValue)

我建议像这样使用 System.Numerics.BigInteger

class InclusiveRandom
{
    private readonly Random rnd = new Random();

    public byte Next(byte min, byte max) => (byte)NextHelper(min, max);
    public sbyte Next(sbyte min, sbyte max) => (sbyte)NextHelper(min, max);
    public short Next(short min, short max) => (short)NextHelper(min, max);
    public ushort Next(ushort min, ushort max) => (ushort)NextHelper(min, max);
    public int Next(int min, int max) => (int)NextHelper(min, max);
    public uint Next(uint min, uint max) => (uint)NextHelper(min, max);
    public long Next(long min, long max) => (long)NextHelper(min, max);
    public ulong Next(ulong min, ulong max) => (ulong)NextHelper(min, max);

    private BigInteger NextHelper(BigInteger min, BigInteger max)
    {
        if (max <= min)
            throw new ArgumentException($"max {max} should be greater than min {min}");

        return min + RandomHelper(max - min);
    }

    private BigInteger RandomHelper(BigInteger bigInteger)
    {
        byte[] bytes = bigInteger.ToByteArray();
        BigInteger random;

        do
        {
            rnd.NextBytes(bytes);
            bytes[bytes.Length - 1] &= 0x7F;
            random = new BigInteger(bytes);
        } while (random > bigInteger);

        return random;
    }
}

我用 sbyte 测试了它。

var rnd = new InclusiveRandom();
var frequency = Enumerable.Range(sbyte.MinValue, sbyte.MaxValue - sbyte.MinValue + 1).ToDictionary(i => (sbyte)i, i => 0ul);
var count = 100000000;
for (var i = 0; i < count; i++)
    frequency[rnd.Next(sbyte.MinValue, sbyte.MaxValue)]++;
foreach (var i in frequency)
    chart1.Series[0].Points.AddXY(i.Key, (double)i.Value / count);

chart1.ChartAreas[0].AxisY.StripLines
    .Add(new StripLine { Interval = 0, IntervalOffset = 1d / 256, StripWidth = 0.0003, BackColor = Color.Red });

分发还可以。


据我所知,你想要Random在-2.147.483.648和+2.147.483.647之间输出一个值。 但问题是随机的,因为这些值只会给出从-2.147.483.648到+2.147.483.64 6的 值,因为最大值是独占的。

选项0:把东西带走,学会没有它

道格拉斯亚当斯不是程序员AFAIK,但他对我们有一些好的建议:“制造任何看不见的技术是如此无限复杂,以至于九亿九千九十九亿九千九百九十九九九十九 - 一万分之一,九亿九千九百九十九分之一,它就更简单,更有效,只是把它拿走而没有它。“

这可能就是这种情况。

选项1:我们需要一个更大的随机!

Random.Next使用Int32作为参数。 我能想到的一个选择是使用不同的随机函数可以将下一个更高级别的整数(Int64)作为输入。 Int32隐式地转换为Int64。 Int64 Number = Int64(Int32.MaxValue)+1;

但是afaik,你必须走出.NET库才能做到这一点。 那时你也可以寻找一个包含Max的随机数。

但我认为有一个数学原因它必须排除一个值。

选项2:滚动更多

另一种方法是使用两个Random调用 - 每个调用范围的一半 - 然后添加它们。

Number1 = rng.Next(-2.147.483.648, 0);
Number2 = rng.Next(0, 2.147.483.647);
resut = Number1 + Number2;

但是,我90%肯定会破坏randon分布。 我的P&P RPG经验给了我一些骰子机会的经验,我知道滚动2个骰子(或相同的2次)会让你得到一个非常不同的结果分布,然后是一个特定的骰子。 如果您不需要此随机分布,那么这是一个选项。 但如果你不太关心他的发行,那值得一试。

选项3:您需要全系列产品吗? 或者你只关心最小值和最大值吗?

我假设您正在进行某种形式的测试,并且您需要Int.MaxValue和Int.MinValue都在该范围内。 但是你是否也需要它们 之间的 所有价值,或者你可以没有其中一个吗? 如果你必须松开一个值,你宁愿失去4而不是Int.MaxValue吗?

Number = rng.Next(Int.MinValue, Int.MaxValue);
if(Number > 3)
  Number = Number +1;

像这样的代码可以获得MinValueand和Maxvalue之间的每个数字, 除了4 。 但在大多数情况下,可以处理3和5的代码也可以处理4.没有必要明确测试4。

当然,asumes 4不是必须运行的一些重要测试数字(由于这些原因,我避免了1和0)。 您还可以决定随机“跳过”的数字:

skipAbleNumber = rng.Next(Int.MinValue +1, Int.MaxValue);

然后使用 > skipAbleNumber 而不是 > 4


没有铸造,不 long ,所有边界情况都考虑在内,性能最佳。

static class RandomExtension
{
    private static readonly byte[] bytes = new byte[sizeof(int)];

    public static int InclusiveNext(this Random random, int min, int max)
    {
        if (max < int.MaxValue)
            // can safely increase 'max'
            return random.Next(min, max + 1);

        // now 'max' is definitely 'int.MaxValue'
        if (min > int.MinValue)
            // can safely decrease 'min'
            // so get ['min' - 1, 'max' - 1]
            // and move it to ['min', 'max']
            return random.Next(min - 1, max) + 1;

        // now 'max' is definitely 'int.MaxValue'
        // and 'min' is definitely 'int.MinValue'
        // so the only option is
        random.NextBytes(bytes);
        return BitConverter.ToInt32(bytes, 0);
    }
}

这可以保证与负值和非负值一起使用:

public static int NextIntegerInclusive(this Random r, int min_value, int max_value)
{
  if (max_value < min_value)
  {
    throw new InvalidOperationException("max_value must be greater than min_value.");
  }
  long offsetFromZero =(long)min_value; // e.g. -2,147,483,648
  long bound = (long)max_value; // e.g. 2,147,483,647
  bound -= offsetFromZero; // e.g. 4,294,967,295 (uint.MaxValue)
  bound += Math.Sign(bound); // e.g. 4,294,967,296 (uint.MaxValue + 1)
  return (int) (Math.Round(r.NextDouble() * bound) + offsetFromZero); // e.g. -2,147,483,648 => 2,147,483,647
}

那这个呢?

   using System;

    public class Example
    {
       public static void Main()
       {
          Random rnd = new Random();
          int min_value = max_value;
          int max_value = min_value;

          Console.WriteLine("\n20 random integers from 10 to 20:");
          for (int ctr = 1; ctr <= 20; ctr++) 
          {
             Console.Write("{0,6}", rnd.Next(min_value, max_value));
             if (ctr % 5 == 0) Console.WriteLine();
          }
       }
    }

Random.Next(int minValue, int maxValue) 内部实现 为大范围生成两个样本,例如 Int32.MinValueInt32.MaxValue 之间的范围。 对于 NextInclusive 方法,我不得不使用另一个大范围 Next ,共计四个样本。 因此,性能应与填充4字节缓冲区的版本(每字节一个样本)相当。

public static class RandomExtensions
{
    public static int NextInclusive(this Random random, int minValue, int maxValue)
    {
        if (maxValue == Int32.MaxValue)
        {
            if (minValue == Int32.MinValue)
            {
                var value1 = random.Next(Int32.MinValue, Int32.MaxValue);
                var value2 = random.Next(Int32.MinValue, Int32.MaxValue);
                return value1 < value2 ? value1 : value1 + 1;
            }
            return random.Next(minValue - 1, Int32.MaxValue) + 1;
        }
        return random.Next(minValue, maxValue + 1);
    }

}

一些结果:

new Random(0).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue
new Random(1).NextInclusive(int.MaxValue - 1, int.MaxValue); // returns int.MaxValue - 1
new Random(0).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue + 1
new Random(1).NextInclusive(int.MinValue, int.MinValue + 1); // returns int.MinValue
new Random(24917099).NextInclusive(int.MinValue, int.MaxValue); // returns int.MinValue
var random = new Random(784288084);
random.NextInclusive(int.MinValue, int.MaxValue);
random.NextInclusive(int.MinValue, int.MaxValue); // returns int.MaxValue

更新: 我的实现在最大可能范围( Int32.MinValue - Int32.MaxValue )中具有平庸的性能,因此我想出了一个新的速度快4倍。 它在我的机器中每秒产生大约22,000,000个随机数。 我认为它不会比这更快。

public static int NextInclusive(this Random random, int minValue, int maxValue)
{
    if (maxValue == Int32.MaxValue)
    {
        if (minValue == Int32.MinValue)
        {
            var value1 = random.Next() % 0x10000;
            var value2 = random.Next() % 0x10000;
            return (value1 << 16) | value2;
        }
        return random.Next(minValue - 1, Int32.MaxValue) + 1;
    }
    return random.Next(minValue, maxValue + 1);
}

一些结果:

new Random(0).NextInclusive(int.MaxValue - 1, int.MaxValue); // = int.MaxValue
new Random(1).NextInclusive(int.MaxValue - 1, int.MaxValue); // = int.MaxValue - 1
new Random(0).NextInclusive(int.MinValue, int.MinValue + 1); // = int.MinValue + 1
new Random(1).NextInclusive(int.MinValue, int.MinValue + 1); // = int.MinValue
new Random(1655705829).NextInclusive(int.MinValue, int.MaxValue); // = int.MaxValue
var random = new Random(1704364573);
random.NextInclusive(int.MinValue, int.MaxValue);
random.NextInclusive(int.MinValue, int.MaxValue);
random.NextInclusive(int.MinValue, int.MaxValue); // = int.MinValue




random