HiveBrain v1.2.0
Get Started
← Back to all entries
snippetcsharpMinor

Algorithm to convert random bytes to integers

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
randomconvertalgorithmbytesintegers

Problem

I'm trying to convert from random bytes to integers within a range. Basically converting as such:

byte[] GetRandomBytes(int count) -> int NextInteger(int min, int max)


Another way to think about it would be: I have a RNGCryptoServiceProvider but would rather have the interface to Random.

My current algorithm works out how many bits it needs based on min and max, gets a random int (after masking off any bits it doesn't need), then loops until it gets a number less than max - min.

Question 1: Is my algorithm sound?

Question 1a: Is the below implementation sound (c#) (specifically: RandomSourceBase.Next(int, int))?

```
using System;
using System.Collections.Generic;
using System.Security.Cryptography;

namespace ConsoleApplication1
{
public abstract class RandomSourceBase
{
public abstract byte[] GetRandomBytes(int numberOfBytes);

public int Next()
{
return Next(0, Int32.MaxValue);
}
public int Next(int maxValue)
{
return Next(0, maxValue);
}
public int Next(int minValue, int maxValue)
{
if (minValue range - 1)
{
var bytes = this.GetRandomBytes(4);
result = (Math.Abs(BitConverter.ToInt32(bytes, 0)) & bitmask) - 1;
}
return result + minValue;
}
}
public class CryptoRandomSource : RandomSourceBase
{
private RNGCryptoServiceProvider _RandomProvider;
public CryptoRandomSource()
{
this._RandomProvider = new RNGCryptoServiceProvider();
}

public override byte[] GetRandomBytes(int numberOfBytes)
{
var result = new byte[numberOfBytes];
this._RandomProvider.GetBytes(result);
return result;
}
}

class Program
{
static void Main(string[] args)
{
TestNextInt32(new CryptoRandomSource(), 50);
TestNext

Solution

Yes, the algorithm as described is sound, although not the most efficient use of the random number source. However, there are a few surprises in the code. Making the default Next() capable of returning 2^31 - 1 distinct values is a bit unexpected, and slightly skews the distribution of the lower bits. It might be worth changing the names, too, in case you want to add more output types later. I would adjust as follows:

public int NextInt32()
    {
        byte[] bytes = GetRandomBytes(4);
        int i = BitConverter.ToInt32(bytes);
        return i & Int32.MaxValue;
    }

    public int NextInt32(int maxExcl)
    {
        if (maxExcl <= 0) throw new ArgumentOutOfRangeException("maxExcl", maxExcl, "maxExcl must be positive");

        // Let k = (Int32.MaxValue + 1) % maxExcl
        // Then we want to exclude the top k values in order to get a uniform distribution
        // You can do the calculations using uints if you prefer to only have one %
        int k = ((Int32.MaxValue % maxExcl) + 1) % maxExcl;
        while (true)
        {
            int rnd = NextInt32();
            if (rnd <= Int32.MaxValue - k)
                return rnd % maxExcl;
        }
    }

    public int NextInt32(int minIncl, int maxExcl)
    {
        if (minIncl < 0)
            throw new ArgumentOutOfRangeException("minIncl", minIncl, "minValue must be non-negative");
        if (maxExcl <= minIncl)
            throw new ArgumentOutOfRangeException("maxExcl", maxExcl, "maxExcl must be greater than minIncl");

        return minIncl + NextInt32(maxExcl - minIncl);
    }

Code Snippets

public int NextInt32()
    {
        byte[] bytes = GetRandomBytes(4);
        int i = BitConverter.ToInt32(bytes);
        return i & Int32.MaxValue;
    }

    public int NextInt32(int maxExcl)
    {
        if (maxExcl <= 0) throw new ArgumentOutOfRangeException("maxExcl", maxExcl, "maxExcl must be positive");

        // Let k = (Int32.MaxValue + 1) % maxExcl
        // Then we want to exclude the top k values in order to get a uniform distribution
        // You can do the calculations using uints if you prefer to only have one %
        int k = ((Int32.MaxValue % maxExcl) + 1) % maxExcl;
        while (true)
        {
            int rnd = NextInt32();
            if (rnd <= Int32.MaxValue - k)
                return rnd % maxExcl;
        }
    }

    public int NextInt32(int minIncl, int maxExcl)
    {
        if (minIncl < 0)
            throw new ArgumentOutOfRangeException("minIncl", minIncl, "minValue must be non-negative");
        if (maxExcl <= minIncl)
            throw new ArgumentOutOfRangeException("maxExcl", maxExcl, "maxExcl must be greater than minIncl");

        return minIncl + NextInt32(maxExcl - minIncl);
    }

Context

StackExchange Code Review Q#6304, answer score: 3

Revisions (0)

No revisions yet.