Results 1 to 1 of 1

Thread: A More Random Random Class

  1. #1

    Thread Starter
    .NUT jmcilhinney's Avatar
    Join Date
    May 2005
    Location
    Sydney, Australia
    Posts
    104,961

    A More Random Random Class

    VB version here.

    The .NET Framework includes the System.Random class, whose job it is to generate random numbers. It does so using a sequence based on a seed value, which is based on the system time by default. There are numerous possible seeds and predicting the exact time that a Random object will be created is not easy. That, coupled with the fact that the distribution of numbers generated by the sequence satisfies various statistical requirements for randomness, means that the Random class is quite suitable for most of your random number needs.

    The main issue with the Random class is that, given a particular seed, the sequence will always produce the same numbers. That can actually be good for some testing scenarios but it does mean that the results could be manipulated. Where very high levels of unpredictability are required, the Random class is not suitable.

    I've seen a number of people use various methods to generate more random random numbers. That's not really necessary though. The .NET Framework also includes the System.Security.Cryptography.RNGCryptoServiceProvider class for generating random numbers. It's output is sufficiently random for use in cryptography, so there's really no need for anything more random than that.

    One issue with the RNGCryptoServiceProvider class, though, is that it populates byte arrays with random values. That's not always completely convenient though, as the most common need in applications is a random int value within a specific range. The Random class is nice and easy to use in that scenario, thanks to its Next method. What if we could combine the ease of use of the Random class with the increased randomness of the RNGCryptoServiceProvider class?

    As it happens, we can. The Random class has a protected method named Sample that generates a double values in the range 0.0 <= x < 1.0. The result of that method is then used in the other methods to generate int values in a range. We can define our own class that inherits Random and overrides that Sample method to use RNGCryptoServiceProvider internally. Here's one I prepared earlier:
    csharp Code:
    1. using System;
    2. using System.Security.Cryptography;
    3.  
    4. /// <summary>
    5. /// Represents a random number generator, a device that produces a sequence of numbers that meet cryptographic requirements for randomness.
    6. /// </summary>
    7. /// <remarks>
    8. /// Seed values are meaningless because this class uses a cryptographic service provider internally rather than a pseudo-random sequence.
    9. /// </remarks>
    10. public class CryptoRandom : Random, IDisposable
    11. {
    12.     /// <summary>
    13.     /// The internal random number generator.
    14.     /// </summary>
    15.     private readonly RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
    16.  
    17.     /// <summary>
    18.     /// Returns a random number between 0.0 and 1.0.
    19.     /// </summary>
    20.     /// <returns>
    21.     /// A double-precision floating point number greater than or equal to 0.0, and less than 1.0.
    22.     /// </returns>
    23.     /// <remarks>
    24.     /// Uses a cryptographic service provider internally rather than a pseudo-random sequence.
    25.     /// </remarks>
    26.     protected override double Sample()
    27.     {
    28.         var data = new byte[8];
    29.         ulong number;
    30.  
    31.         do
    32.         {
    33.             // Get 8 random bytes.
    34.             rng.GetBytes(data);
    35.  
    36.             // Convert the bytes to an unsigned 64-bit number.
    37.             number = BitConverter.ToUInt64(data, 0);
    38.         }
    39.         while (number == ulong.MaxValue); // The result must be less than 1.0
    40.  
    41.         // Divide the number by the largest possible unsigned 64-bit number to get a value in the range 0.0 <= N < 1.0.
    42.         return Convert.ToDouble(number) / Convert.ToDouble(ulong.MaxValue);
    43.     }
    44.  
    45.  
    46. #region IDisposable Support
    47.  
    48.     private bool disposedValue; // To detect redundant calls
    49.  
    50.     // IDisposable
    51.     protected virtual void Dispose(bool disposing)
    52.     {
    53.         if (!this.disposedValue)
    54.         {
    55.             if (disposing)
    56.             {
    57.                 // Dispose the underlying cryptographic random number generator.
    58.                 rng.Dispose();
    59.             }
    60.         }
    61.  
    62.         this.disposedValue = true;
    63.     }
    64.  
    65.     // This code added to correctly implement the disposable pattern.
    66.     public void Dispose()
    67.     {
    68.         // Do not change this code.  Put cleanup code in Dispose(bool disposing) above.
    69.         Dispose(true);
    70.         GC.SuppressFinalize(this);
    71.     }
    72.  
    73. #endregion
    74. }
    You can create an instance of that class and use it in pretty much exactly the same way as you would a Random object. The only difference is that you will need to call its Dispose method when you're done, in order to dispose the internal RNGCryptoServiceProvider object. Sample usage:
    csharp Code:
    1. public partial class Form1 : Form
    2. {
    3.     public Form1()
    4.     {
    5.         InitializeComponent();
    6.     }
    7.  
    8.     private readonly CryptoRandom rng = new CryptoRandom();
    9.  
    10.     private void button1_Click(object sender, EventArgs e)
    11.     {
    12.         // Generate 10 random numbers.
    13.         var numbers = new int[10];
    14.  
    15.         for (int i = 0; i < numbers.Length; i++)
    16.         {
    17.             // Generate numbers in the range 0 <= n <= 1000
    18.             numbers[i] = rng.Next(1001);
    19.         }
    20.  
    21.         MessageBox.Show(string.Join(Environment.NewLine, numbers), "10 Random Numbers");
    22.     }
    23.  
    24.     private void Form1_FormClosed(object sender, FormClosedEventArgs e)
    25.     {
    26.         rng.Dispose();
    27.     }
    28. }
    Attached Files Attached Files
    Last edited by jmcilhinney; Oct 30th, 2015 at 03:30 AM. Reason: Fixed bug in Sample method that caused integer division instead of floating point division.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width