Recently had reason to design a range based random number generator that uses the RNGCryptoServiceProvider provided by the .net api.
The basic algorithm generates a random unsigned 64 bit integer and creates a floating point number between 0 and 1 by dividing it by UInt64.MaxValue+ 1. This is then multiplied by the range and added to the minimum, to produce a 32 bit integer in the right range.
The class also includes a shuffle method.
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Linq;
public static class CryptoRandom
{
const double MAX_RANGE = (double)UInt64.MaxValue + 1;
/// <summary>
/// Get a cryptographic random integer in the range from
/// min(inclusive) to max(exclusive)
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
/// <returns></returns>
public static int Next(int min, int max)
{
if (max < min)
{
throw new ArgumentException("max is less than min");
}
if(min < 0)
{
throw new ArgumentException("min and max must be positive integers");
}
if (min == max)
{
return min;
}
using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
{
byte[] randomNumber = new byte[8];
rng.GetBytes(randomNumber);
double baseNum = BitConverter.ToUInt64(randomNumber, 0) / MAX_RANGE;
int range = max - min;
return (int)(baseNum * range) + min;
}
}
/// <summary>
/// Get a cryptographic random integer in the range from
/// 0 to max(exclusive)
/// </summary>
/// <param name="max"></param>
/// <returns></returns>
public static int Next(int max)
{
return Next(0, max);
}
/// <summary>
/// Get a cryptographic random 32-bit integer
/// </summary>
/// <returns></returns>
public static int Next()
{
return Next(0, Int32.MaxValue);
}
/// <summary>
/// A cryptographic random shuffle method
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="input"></param>
/// <returns></returns>
public static ICollection<T> Shuffle<T>(ICollection<T> input)
{
return input.OrderBy(x => Next()).ToArray();
}
}