سؤال

I am trying to write a function that uses the default RandomNumberGenerator implementation to generate Int32 values within a specified range.

void GenerateRandom (int [] data, int minInclusive, int maxExclusive)
{
    int size = 0;
    int length = 0;
    byte [] bytes = null;

    size = (int) Math.Ceiling(Math.Log(Math.Abs(maxExclusive - minInclusive), 2));
    length = data.Length * size;
    var bytes = new byte [length];

    using (RandomNumberGenerator generator = RandomNumberGenerator.Create())
    {
        generator.GetBytes(bytes);
    }

    // How to effectively convert this `byte []` to an `int []` within the specified range?
}

One attempt was to generate a random byte array of length (data.Length * ((int) Math.Ceiling(Math.Log(Math.Abs(maxExclusive - minInclusive), 2)))) and combine each x number of bytes to an int. Irrespective of specified range, this approach of course has the disadvantage of a huge bias towards larger values since there is little chance of multiple most significant bytes being zero.

Any input would be appreciated. Although I'm using .NET here, the platform/language doesn't matter. Looking for a conceptual hint.

Please note that I am already familiar with the Random class in .NET but am only interested in figuring out how to do this manually while being able to use the RandomNumberGenerator.

هل كانت مفيدة؟

المحلول

unsafe static int[] GenerateRandom(int length, int minInclusive, int maxExclusive)
{
    var bytes = new byte[length * 4];
    var ints = new int[length];

    var ratio = uint.MaxValue / (double)(maxExclusive - minInclusive);

    using (RandomNumberGenerator generator = RandomNumberGenerator.Create())
    {
        generator.GetBytes(bytes);
        fixed(byte* b = bytes)
        {
            uint* i = (uint*)b;
            for(int j = 0; j < length; j++, i++)
            {
                ints[j] = minInclusive + (int)(*i / ratio);
            }
        }
    }

    return ints;
}

I've run a little test:

var ints = GenerateRandom(1000000, 0, 300);

var groups = ints.GroupBy(x => x).Select(g => new { value = g.Key, count = g.Count() });
var hist = Enumerable.Range(0, 300).Join(groups, x => x, g => g.value, (x, g) => new { value = x, count = g.count }).ToList();

var max = hist.OrderByDescending(x => x.value).First();
var min = hist.First();

And results are quite random across all numbers between 0 and 300 with min.count = 3301 and max.count = 3358.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top