Skip to content

Instantly share code, notes, and snippets.

@craigtp
Last active June 13, 2018 22:51
Show Gist options
  • Save craigtp/9889530 to your computer and use it in GitHub Desktop.
Save craigtp/9889530 to your computer and use it in GitHub Desktop.
BetterRandom - A C# class to produce random numbers which inherits from the .NET framework's System.Random class and so can be a drop-in replacement for usages of the standard Random class, but which uses the RNGCryptoServiceProvider to generate better (and cryptographically secure) random numbers.
using System;
using System.Security.Cryptography;
namespace BetterRandomNumbers
{
// BetterRandom.cs
// This class implements a random number generator that is based off the Windows "Next Generation" cryptographically secure
// random number generator. It inherits from the base Random class, so can be used as a "drop-in" replacement for the
// built-in .NET System.Security.Random class, but providing a superior quality of random numbers.
public class BetterRandom : Random, IDisposable
{
private const int BufferSize = 1024; // must be a multiple of 4
private readonly byte[] _randomBuffer;
private int _bufferOffset;
private RNGCryptoServiceProvider rng;
public BetterRandom()
{
_randomBuffer = new byte[BufferSize];
rng = new RNGCryptoServiceProvider();
_bufferOffset = _randomBuffer.Length;
}
private void FillBuffer()
{
rng.GetBytes(_randomBuffer);
_bufferOffset = 0;
}
public override int Next()
{
if (_bufferOffset >= _randomBuffer.Length)
{
FillBuffer();
}
var val = BitConverter.ToInt32(_randomBuffer, _bufferOffset) & 0x7fffffff;
_bufferOffset += sizeof(int);
return val;
}
public override int Next(int maxValue)
{
return Next() % maxValue;
}
public override int Next(int minValue, int maxValue)
{
if (maxValue < minValue)
{
throw new ArgumentOutOfRangeException("maxValue", "maxValue must be greater than or equal to minValue");
}
var range = maxValue - minValue;
return minValue + Next(range);
}
public override double NextDouble()
{
var val = Next();
return (double)val / int.MaxValue;
}
public void GetBytes(byte[] buff)
{
rng.GetBytes(buff);
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (rng != null)
{
rng.Dispose();
rng = null;
}
}
GC.SuppressFinalize(this);
}
}
}
@craigtp
Copy link
Author

craigtp commented Feb 11, 2016

Not really, however you're free to use it however you please.

@lellis1936
Copy link

lellis1936 commented Jun 13, 2018

The output of BetterRandom.Next(int maxValue) is biased, and the bias will be large if the range is large.

See https://ericlippert.com/2013/12/16/how-much-bias-is-introduced-by-the-remainder-technique/

The problems is this code:

 return Next() % maxValue;

Something like this idea is better (not tested):

while(true)
{
  int value = Next());
  if (value < int.MaxValue - int.MaxValue % maxValue)
    return value % maxValue;
}

I have concerns about NextDouble too, even with this fix.

After all your objective is to produce better random output than the built-in Random class does...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment