snippetcsharpMinor
Algorithm to convert random bytes to integers
Viewed 0 times
randomconvertalgorithmbytesintegers
Problem
I'm trying to convert from random bytes to integers within a range. Basically converting as such:
Another way to think about it would be: I have a
My current algorithm works out how many bits it needs based on
Question 1: Is my algorithm sound?
Question 1a: Is the below implementation sound (c#) (specifically:
```
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
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.