While looking for best attempts at generating truly random numbers, I stumbled upon this code example.
Looking for opinions on this snippet.
<I really do not suggest using provided example. Although RNGCryptoServiceProvider
returns truly good random (or at least it should), but the same is not true for Random
. Moreover - it is not known if Random(value)
creates true bijection against value returned by Next(...)
. Moreover - it is not guaranteed that Next(min, max)
returns the value in a truly random manner (meaning equal chances for number to hit each value).
I would first tear down the problem to getting a number in the interval 0 - max (exclusive). Then, I would use nearest power of 2 to get a random value in the range 0 - (2^n - 1). Now, one thing you MUST never do here is use modulo to get a number in the preferred range, like rand(0 - (2^n - 1)) % max
, because by doing, so you are actually increasing chances of getting number in lower range.
Example: max = 3, n = 2 (0 - (2^2 - 1)) % 2
, numbers (0, 1, 2, 3), corresponding values after modulo (0, 1, 2, 0). See that we hit 0 twice, which is really bad randomness.
So, the solution would be to use crypto random to get a value to nearest power of two, and in case the value is outside maximum range, repeat procedure (get another crypto random) until the value is inside the given range. This would be a much better algorithm.
I think this is a more efficient, and possibly faster generator then the ones listed above..
public static class SecureRandom
{
#region Constants
private const int INT_SIZE = 4;
private const int INT64_SIZE = 8;
#endregion
#region Fields
private static RandomNumberGenerator _Random;
#endregion
#region Constructor
static SecureRandom()
{
_Random = new RNGCryptoServiceProvider();
}
#endregion
#region Random Int32
/// <summary>
/// Get the next random integer
/// </summary>
/// <returns>Random [Int32]</returns>
public static Int32 Next()
{
byte[] data = new byte[INT_SIZE];
Int32[] result = new Int32[1];
_Random.GetBytes(data);
Buffer.BlockCopy(data, 0, result, 0, INT_SIZE);
return result[0];
}
/// <summary>
/// Get the next random integer to a maximum value
/// </summary>
/// <param name="MaxValue">Maximum value</param>
/// <returns>Random [Int32]</returns>
public static Int32 Next(Int32 MaxValue)
{
Int32 result = 0;
do
{
result = Next();
} while (result > MaxValue);
return result;
}
#endregion
#region Random UInt32
/// <summary>
/// Get the next random unsigned integer
/// </summary>
/// <returns>Random [UInt32]</returns>
public static UInt32 NextUInt()
{
byte[] data = new byte[INT_SIZE];
Int32[] result = new Int32[1];
do
{
_Random.GetBytes(data);
Buffer.BlockCopy(data, 0, result, 0, INT_SIZE);
} while (result[0] < 0);
return (UInt32)result[0];
}
/// <summary>
/// Get the next random unsigned integer to a maximum value
/// </summary>
/// <param name="MaxValue">Maximum value</param>
/// <returns>Random [UInt32]</returns>
public static UInt32 NextUInt(UInt32 MaxValue)
{
UInt32 result = 0;
do
{
result = NextUInt();
} while (result > MaxValue);
return result;
}
#endregion
#region Random Int64
/// <summary>
/// Get the next random integer
/// </summary>
/// <returns>Random [Int32]</returns>
public static Int64 NextLong()
{
byte[] data = new byte[INT64_SIZE];
Int64[] result = new Int64[1];
_Random.GetBytes(data);
Buffer.BlockCopy(data, 0, result, 0, INT64_SIZE);
return result[0];
}
/// <summary>
/// Get the next random unsigned long to a maximum value
/// </summary>
/// <param name="MaxValue">Maximum value</param>
/// <returns>Random [UInt64]</returns>
public static Int64 NextLong(Int64 MaxValue)
{
Int64 result = 0;
do
{
result = NextLong();
} while (result > MaxValue);
return result;
}
#endregion
#region Random UInt32
/// <summary>
/// Get the next random unsigned long
/// </summary>
/// <returns>Random [UInt64]</returns>
public static UInt64 NextULong()
{
byte[] data = new byte[INT64_SIZE];
Int64[] result = new Int64[1];
do
{
_Random.GetBytes(data);
Buffer.BlockCopy(data, 0, result, 0, INT64_SIZE);
} while (result[0] < 0);
return (UInt64)result[0];
}
/// <summary>
/// Get the next random unsigned long to a maximum value
/// </summary>
/// <param name="MaxValue">Maximum value</param>
/// <returns>Random [UInt64]</returns>
public static UInt64 NextULong(UInt64 MaxValue)
{
UInt64 result = 0;
do
{
result = NextULong();
} while (result > MaxValue);
return result;
}
#endregion
#region Random Bytes
/// <summary>
/// Get random bytes
/// </summary>
/// <param name="data">Random [byte array]</param>
public static byte[] NextBytes(long Size)
{
byte[] data = new byte[Size];
_Random.GetBytes(data);
return data;
}
#endregion
}
It really depends on the intended use or requirement of the random number being generated.
The Random class is useful for practical randomization like randomizing the order images display in an image rotator or rolls of a die.
If, on the other hand, you need random numbers requiring a greater amount of security, like to generate a password or payment confirmation key, then using a class such as RNGCryptoServiceProvider or creating your own implementation of the abstract class RandomNumberGenerator that implements a cryptographic algorithm are better alternatives.
I felt like this could be a lot simpler and achieved in fewer lines of code so I'll throw my effort in - this will always return a positive number between 0 and 1:
public double Random()
{
using var csp = new RNGCryptoServiceProvider();
byte[] b = new byte[8];
csp.GetBytes(b);
var lng = BitConverter.ToInt64(b, 0);
var dbl = (double)(lng < 0 ? ~lng : lng);
// Convert to a random number between 0 and 1
return dbl / long.MaxValue;
}
Instead of creating a new RNGCryptoServiceProvider
each time, a simple static field would do fine.
To return a random positive integer between two numbers this works fine - though you need to check min
is less than max
, and that both are positive:
public long RandomInt64(long min = 0, long max = long.MaxValue)
{
// Check arguments
if (min >= max)
{
throw new ArgumentOutOfRangeException(nameof(min), min, "Minimium value must be less than the maximum value.");
}
if (min < 0)
{
throw new ArgumentException("Minimum value must be at least 0.", nameof(min));
}
// Get the range between the specified minimum and maximum values
var range = max - min;
// Now add a random amount of the range to the minimum value - it will never exceed maximum value
var add = Math.Round(range * Random());
return (long)(min + add);
}
Well, using RNGCryptoServiceProvider
gives you an unguessable crypto-strength seed whereas Environment.TickCount
is, in theory, predictable.
Another crucial difference would be evident when calling your NextInt
method several times in quick succession. Using RNGCryptoServiceProvider
will seed the Random
object with a different crypto-strength number each time, meaning that it will go on to return a different random number for each call. Using TickCount
risks seeding the Random
object with the same number each time (if the method is called several times during the same "tick"), meaning that it will go on to return the same (supposedly random) number for each call.
If you genuinely need truly random numbers then you shouldn't be using a computer to generate them at all: you should be measuring radioactive decay or something similarly, genuinely unpredictable.
Ok, so I'm a little late to the party, but I really wanted a full implementation of System.Random that can be called multiple times during the same timer tic and yield different results. After much agonizing over different implementations, I settled on the simplest one that I came up with, which provides a default constructor that supplies a random key to the base System.Random constructor:
/// <summary> An implementation of System.Random whose default constructor uses a random seed value rather than the system time. </summary>
public class RandomEx : Random
{
/// <summary> Initializes a new CryptoRandom instance using a random seed value. </summary>
public RandomEx()
: base(_GetSeed())
{ }
/// <summary> Initializes a new CryptoRandom instance using the specified seed value. </summary>
/// <param name="seed"> The seed value. </param>
public RandomEx(int seed)
: base(seed)
{ }
// The static (shared by all callers!) RandomNumberGenerator instance
private static RandomNumberGenerator _rng = null;
/// <summary> Static method that returns a random integer. </summary>
private static int _GetSeed()
{
var seed = new byte[sizeof(int)];
lock (typeof(RandomEx)) {
// Initialize the RandomNumberGenerator instance if necessary
if (_rng == null) _rng = new RNGCryptoServiceProvider();
// Get the random bytes
_rng.GetBytes(seed);
}
// Convert the bytes to an int
return BitConverter.ToInt32(seed, 0);
}
}
Along the way, I also wrote and tested an implementation that overrides the methods necessary to use RNGCryptoServiceProvider to provide ALL of the random values (rather than relying on whatever random number generator is baked into the System.Random class). But I have no idea how cryptographically strong the results are by the time you take my random Sample() values and push 'em through the transformations to produce integer values. Anyway, here is the code if anyone wants it:
/// <summary> An implementation of System.Random that uses RNGCryptoServiceProvider to provide random values. </summary>
public class CryptoRandom : Random, IDisposable
{
// Class data
RandomNumberGenerator _csp = new RNGCryptoServiceProvider();
/// <summary> Returns a random number between 0.0 (inclusive) and 1.0 (exclusive). </summary>
protected override double Sample()
{
// Get a nonnegative random Int64
byte[] bytes = new byte[sizeof(long)];
_csp.GetBytes(bytes);
long value = BitConverter.ToInt64(bytes, 0) & long.MaxValue;
// Scale it to 0->1
return (double)value / (((double)Int64.MaxValue) + 1025.0d);
}
/// <summary> Fills the elements of the specified array of bytes with random numbers. </summary>
/// <param name="buffer"> An array of bytes to contain random numbers. </param>
public override void NextBytes(byte[] buffer)
{
_csp.GetBytes(buffer);
}
/// <summary> Returns a nonnegative random integer. </summary>
/// <returns> A 32-bit signed integer greater than or equal to zero. </returns>
public override int Next()
{
byte[] data = new byte[4];
_csp.GetBytes(data);
data[3] &= 0x7f;
return BitConverter.ToInt32(data, 0);
}
/// <summary> Returns a random integer that is within a specified range. </summary>
/// <param name="minValue"> The inclusive lower bound of the random number returned. </param>
/// <param name="maxValue"> The exclusive upper bound of the random number returned. maxValue must be greater than or equal to minValue. </param>
/// <returns> A 32-bit signed integer greater than or equal to minValue and less than maxValue; that is, the range of return values includes minValue but not maxValue. If minValue equals maxValue, minValue is returned. </returns>
public override int Next(int minValue, int maxValue)
{
// Special case
if (minValue == maxValue) return minValue;
double sample = Sample();
double range = (double)maxValue - (double)minValue;
return (int)((sample * (double)range) + (double)minValue);
}
#region IDisposible implementation
/// <summary> Disposes the CryptoRandom instance and all of its allocated resources. </summary>
public void Dispose()
{
// Do the actual work
Dispose(true);
// This object will be cleaned up by the Dispose method. Call GC.SupressFinalize to
// take this object off the finalization queue and prevent finalization code for this object
// from executing a second time.
GC.SuppressFinalize(this);
}
// Dispose(bool disposing) executes in two distinct scenarios:
//
// If disposing is true, the method has been called directly or indirectly by a user's code and both
// managed and unmanaged resources can be disposed.
//
// If disposing is false, the method has been called by the runtime from inside the finalizer.
// In this case, only unmanaged resources can be disposed.
protected virtual void Dispose(bool disposing)
{
if (disposing) {
// The method has been called directly or indirectly by a user's code; dispose managed resources (if any)
if (_csp != null) {
_csp.Dispose();
_csp = null;
}
// Dispose unmanaged resources (if any)
}
}
#endregion
}