Cambia Research - Supporting the Microsoft .NET Developer Community Supporting the Microsoft .NET Developer Community  

     | Home  | Articles  | Categories  | Coders  | Search  | Submit  | Contact Us    
Measuring programming progress by lines of code is like measuring aircraft building progress by weight. --Bill Gates

Share Your Knowledge! -- Create and submit your articles the easy way with WebWriter.

Updated:06:19 PM CT Jan 09, 2007
Posted:05:08 PM CT Jan 09, 2007

C# RandomProvider Class

A C# class to provide random numbers for a variety of data types and statistical distributions

Author: Steve Lautenschlager

SnippetC#Tools.NETMathStatistics

 Summary

The .NET Framework provides System.Random for generating random numbers which provides uniform random numbers of type System.Double in the range [0,1). However, in practice one almost always needs random numbers in a different range and often of a different type. I created the RandomProvider class as a more useful wrapper for the Random class and I use it all the time.

Example: How to use the RandomProvider Class

public static void TestRandomProvider()
{
   // Random double [0,1)
   Console.WriteLine("Random Double [0,1) = " + RandomProvider.Next());

   // Random Int64 [50,100)
   long ran = RandomProvider.Next(50,100);
   Console.WriteLine("Random Int64 [50,100) = " + ran);

   // Random Boolean
   Console.WriteLine("Random Boolean = " + RandomProvider.NextBoolean());

   // Random DateTime [1/1/1998, 6/1/1998)
   DateTime minDT = DateTime.Parse("1/1/1998");
   DateTime maxDT = DateTime.Parse("6/1/1998");
   DateTime ranDT = RandomProvider.Next(minDT, maxDT);
   Console.WriteLine("Random DateTime [1/1/1998, 6/1/1998) = " + ranDT);

   // Random TimeSpan [3 hours, 7 hours)
   TimeSpan minTs = new TimeSpan(3, 0, 0);
   TimeSpan maxTs = new TimeSpan(7, 0, 0);
   TimeSpan ranTs = RandomProvider.Next(minTs, maxTs);
   Console.WriteLine("Random TimeSpan [3 hrs, 7 hrs) = " + ranTs);

   // Random Normal Deviate
   Console.WriteLine("Normal deviate = " 
      + RandomProvider.NextNormal());

   // Random Exponential Deviate
   Console.WriteLine("Exponential deviate = " 
      + RandomProvider.NextExponential());

}
If you run the previous method you'll get something similar to the following output:

Example: Output

Random Double [0,1) = 0.147433493820687
Random Int64 [50,100) = 69
Random Boolean = False
Random DateTime [1/1/1998, 6/1/1998) = 2/4/1998 4:09:50 AM
Random TimeSpan [3 hrs, 7 hrs) = 03:58:55.0844951
Normal deviate = 0.277308108809515
Exponential deviate = 1.37058190966813
Here's the class:

The C# RandomProvider Class:

using System;

namespace Cambia.CoreLib
{

   /// <summary>
   /// RandomProvider.  Provides random numbers of all data types
   /// in specified ranges.  It also contains a couple of methods
   /// from Normally (Gaussian) distributed random numbers and 
   /// Exponentially distributed random numbers.
   /// </summary>
   public  class RandomProvider
   {
      private static Random m_RNG1;
      private static double m_StoredUniformDeviate;
      private static bool m_StoredUniformDeviateIsGood = false;

      #region -- Construction/Initialization --

      static RandomProvider()
      {
         Reset();
      }
      public static void Reset()
      {
         m_RNG1 = new Random(Environment.TickCount);
      }

      #endregion

      #region -- Uniform Deviates --

      /// <summary>
      /// Returns double in the range [0, 1)
      /// </summary>
      public static double Next()
      {
         return m_RNG1.NextDouble();
      }

      /// <summary>
      /// Returns true or false randomly.
      /// </summary>
      public static bool NextBoolean()
      {
         if (m_RNG1.Next(0,2) == 0)
            return false;
         else
            return true;
      }

      /// <summary>
      /// Returns double in the range [0, 1)
      /// </summary>
      public static double NextDouble()
      {
         double rn = m_RNG1.NextDouble();
         return rn;
      }

      /// <summary>
      /// Returns Int16 in the range [min, max)
      /// </summary>
      public static Int16 Next(Int16 min, Int16 max)
      {
         if (max <= min) 
         {
            string message = "Max must be greater than min.";
            throw new ArgumentException(message);
         }
         double rn = (max*1.0 - min*1.0)*m_RNG1.NextDouble() + min*1.0;
         return Convert.ToInt16(rn);
      }

      /// <summary>
      /// Returns Int32 in the range [min, max)
      /// </summary>
      public static int Next(int min, int max)
      {
         return m_RNG1.Next(min, max);
      }

      /// <summary>
      /// Returns Int64 in the range [min, max)
      /// </summary>
      public static Int64 Next(Int64 min, Int64 max)
      {
         if (max <= min) 
         {
            string message = "Max must be greater than min.";
            throw new ArgumentException(message);
         }

         double rn = (max*1.0 - min*1.0)*m_RNG1.NextDouble() + min*1.0;
         return Convert.ToInt64(rn);
      }

      /// <summary>
      /// Returns float (Single) in the range [min, max)
      /// </summary>
      public static Single Next(Single min, Single max)
      {
         if (max <= min) 
         {
            string message = "Max must be greater than min.";
            throw new ArgumentException(message);
         }

         double rn = (max*1.0 - min*1.0)*m_RNG1.NextDouble() + min*1.0;
         return Convert.ToSingle(rn);
      }

      /// <summary>
      /// Returns double in the range [min, max)
      /// </summary>
      public static double Next(double min, double max)
      {
         if (max <= min) 
         {
            string message = "Max must be greater than min.";
            throw new ArgumentException(message);
         }

         double rn = (max - min)*m_RNG1.NextDouble() + min;
         return rn;
      }

      /// <summary>
      /// Returns DateTime in the range [min, max)
      /// </summary>
      public static DateTime Next(DateTime min, DateTime max)
      {
         if (max <= min) 
         {
            string message = "Max must be greater than min.";
            throw new ArgumentException(message);
         }
         long minTicks = min.Ticks;
         long maxTicks = max.Ticks;
         double rn = (Convert.ToDouble(maxTicks) 
            - Convert.ToDouble(minTicks))*m_RNG1.NextDouble() 
            + Convert.ToDouble(minTicks);
         return new DateTime(Convert.ToInt64(rn));
      }

      /// <summary>
      /// Returns TimeSpan in the range [min, max)
      /// </summary>
      public static TimeSpan Next(TimeSpan min, TimeSpan max)
      {
         if (max <= min) 
         {
            string message = "Max must be greater than min.";
            throw new ArgumentException(message);
         }

         long minTicks = min.Ticks;
         long maxTicks = max.Ticks;
         double rn = (Convert.ToDouble(maxTicks) 
            - Convert.ToDouble(minTicks))*m_RNG1.NextDouble() 
            + Convert.ToDouble(minTicks);
         return new TimeSpan(Convert.ToInt64(rn));
      }

      /// <summary>
      /// Returns double in the range [min, max)
      /// </summary>
      public static double NextUniform()
      {
         return Next();
      }

      /// <summary>
      /// Returns a uniformly random integer representing one of the values 
      /// in the enum.
      /// </summary>
      public static int NextEnum(Type enumType)
      {
         int[] values = (int[])Enum.GetValues(enumType);
         int randomIndex = Next(0, values.Length);
         return values[randomIndex];
      }

      #endregion

      #region -- Exponential Deviates --

      /// <summary>
      /// Returns an exponentially distributed, positive, random deviate 
      /// of unit mean.
      /// </summary>
      public static double NextExponential()
      {
         double dum = 0.0;
         while (dum == 0.0)
            dum=NextUniform();
         return -1.0*System.Math.Log(dum, System.Math.E);
      }

      #endregion

      #region -- Normal Deviates --

      /// <summary>
      /// Returns a normally distributed deviate with zero mean and unit 
      /// variance.
      /// </summary>
      public static double NextNormal()
      {
         // based on algorithm from Numerical Recipes
         if (m_StoredUniformDeviateIsGood)
         {
            m_StoredUniformDeviateIsGood = false;
            return m_StoredUniformDeviate;
         }
         else
         {
            double rsq = 0.0;
            double v1 = 0.0, v2 = 0.0, fac = 0.0;
            while (rsq >=1.0 || rsq == 0.0)
            {
               v1 = 2.0*Next() - 1.0;
               v2 = 2.0*Next() - 1.0;
               rsq = v1*v1 + v2*v2;
            }
            fac = System.Math.Sqrt(-2.0
               *System.Math.Log(rsq, System.Math.E)/rsq);
            m_StoredUniformDeviate = v1*fac;
            m_StoredUniformDeviateIsGood = true;
            return v2*fac;
         }
      }

      #endregion

   }
}

Add New Comment
OverflowException occurred
Orator29 Aug 07, 6:30Reply 
re: OverflowException occurred
Steve Lautenschlager29 Aug 07, 19:36Reply 
Invalid Range
Orator30 Aug 07, 21:53Reply 
CR Comments by Cambia Research
advertisement
 
Steve Lautenschlager (steve)
Steve is the founder and creator of Cambia Research. Developing and maintaining the site combines his passions for technology, writing and education.
Steve holds a Ph.D. in particle physics from Duke University, has worked at CERN, the European center for particle physics (where the web was born) and in Microsoft's web division with microsoft.com, msnbc.com and other web properties. Steve is a web consultant specializing in Microsoft.NET technologies. Read more here.


 
Copyright © Cambia Research 2002-2007. All Rights Reserved. steve [ at ] cambiaresearch.com