A Good 31 bit Random Number Generator Class

发表于:2007-07-01来源:作者:点击数: 标签:
Latest Snippet Version: 1.0 using System; namespace Barsoom { /// summary /// Good 31-bit Random Number Generator, returns Uint. /// /summary /// remarks /// /// LICENSE : PUBLIC DOMAIN /// /// This is the base class for double and other st
Latest Snippet Version: 1.0

using System;

namespace Barsoom
{
    /// <summary>
    /// Good 31-bit Random Number Generator, returns Uint.
    /// </summary>
    /// <remarks>
    ///
    /// LICENSE : PUBLIC DOMAIN
    ///
    /// This is the base class for double and other statistical
    /// random number distribution classes
    ///
    /// Throws exception if initial seed is out of range.
    ///
    ///    Stephen K. Park and Keith W. Miller. RANDOM NUMBER GENERATORS:
    ///    GOOD ONES ARE HARD TO FIND. Communications of the ACM,
    ///    New York, NY.,October 1988 p.1192
    ///
    /// Throws exception if initial seed is out of range
    ///    This program is public domain and was written by Jill England
    ///    (Oct 1988).
    ///    
    /// Modifications;
    ///    
    /// Converted to C# from C++ and hacked heavily.  One major cool thing done
    /// here is to be able to SET the seed or get the next LCG value.
    /// 3/4/2002 JAE Jill@eskimo.com
    ///
    ///    Imported into Visual Studio .net, 3/1/2002 JAE jill@eskimo.com
    ///    
    /// To build the generator with the original ACM
    /// article@#s numbers use -DACM_ORIGINAL_NUMBERS when compiling.
    ///
    /// Original_numbers are those published m and q in the
    /// original ACM article.  John Burton, a Miller/Park, associate
    /// has furnished numbers for a reportedly better generator.  The
    /// new numbers are now used in this program by default.
    ///
    /// F(z)    = (az)%m
    ///            = az-m(az/m)
    ///
    /// F(z) =   G(z) + mT(z)
    /// G(z) = a(z%q) - r(z/q)
    /// T(z) =  (z/q) - (az/m)
    ///
    /// F(z) = a(z%q)- rz/q+ m((z/q) - a(z/m))
    ///      = a(z%q)- rz/q+ m(z/q) - az
    ///      
    /// NOTE JAE:    I could easily  extend this class to a C# 63 bit LCG but,
    ///                don@#t really see the point for most applications I have.
    ///                Besides 2**63 -1 is 9,223,372,036,854,775,807 that@#s 19
    ///                digits of precision.  A double only has 15- 16 digits
    ///                of precision.  Since my typical use is a double that@#s
    ///                at least 3 digits of precision I could never use. (of
    ///                course 2**31 -1 is only 10 digits of precision so that@#s
    ///                five less than I could use ...)
    ///               
    ///                Advantages of a 64 bit generator would be the insanely
    ///                long cycle until it repeats.  Disadvantages would be
    ///                the implementation complexities of picking correct/good
    ///                m and q values, then testing it.  I@#ll probabaly create
    ///                the class but will be unable to suclearcase/" target="_blank" >ccessfully test the numbers
    ///                for it.  Besides I know the current values work great so
    ///                number I picked, without months of testing, would almost
    ///                certainly not be better.
    ///                
    ///                Of course, the other thing I could do, since this is C#,
    ///                    is to not use Miller and Park@#s algorithm at all but still
    ///                    use their values because the math module in C# is good
    ///                    enough to not make 32 bit math mistakes. I@#ve left it this way
    ///                however because it makes the implementation almost bullet
    ///                proof.
    ///                
    ///                Always be sure to test your implementation,
    ///                you never can tell when intel or someone else will sneak
    ///                a bad floating point unit into your machine.  If the tests
    ///                pass you are probaly ok.
    ///
    ///                <borg-joke>
    ///                "I am Pentium of Borg,
    ///                 Division is Futile,
    ///                 You will be approximated!"
    ///             </borg-joke>
    ///    </remarks>
    public class RNDuint
    {
        protected uint seed;

#if(ACM_ORIGINAL_NUMBERS)
        ///<remarks> This version uses the original ACM m and q.
        ///</remarks>
        static protected uint   m = 2147483647;
        static protected uint   q = 127773;
        static protected ushort a = 16807;
        static protected ushort r =  2836;
        static protected uint   successfulltest = 1043618065;
#else
        ///<remarks>
        /// 1992 -- This version contains NEW numbers provided by John Burton
        /// that improve the distribution over the originally published
        ///    numbers in the ACM. JAE jill@eskimo.com
        ///    </remarks>
        ///    <summary>modulus 2**31 -1</summary>
        static protected uint   m = 2147483647;
        static protected uint   q = 44488;
        static protected ushort a = 48271;
        static protected ushort r =  3399;
        static protected uint   successfulltest = 399268537;
#endif

        protected void uintCheckSeed(uint  initialseed)
        {
            if ( initialseed >= m )
            {
                    throw(new ArgumentOutOfRangeException(
                                "initialSeed",
                                initialseed,
                                "Invalid seed: Must be between 0 and 2,147,483,647")
                    );
            }
            seed = initialseed;
        }


        public RNDuint()
        {
            seed = 1;
        }
        
        /// <summary>
        /// Accepts inter seed from0 to 2**31 -1.
        /// </summary>
        /// <param name="initialseed">Value between 0 and 2,147,438,647.</param>
        public RNDuint( uint  initialseed )
        {
            uintCheckSeed(initialseed);
        }  

        /// <summary>
        /// Get next value or set seed. Range uint 0 to 2**31-1.
        /// </summary>
        public uint n
        {
            get
            {
                int lo, hi, test;

                hi   = (int)(seed/q);
                lo   = (int)(seed%q);

                test = a*lo - r*hi;

                if (test > 0)
                    seed = (uint)test;
                else
                    seed = (uint)test+ m;

                return seed;
            }
            
            set
            {
                uintCheckSeed(value);
            }
        }

        /// <summary>
        /// Tests this class for correct compiler implementation.
        /// </summary>
        /// <returns>False means DO NOT USE this generator.</returns>
        public bool test()
        {
            // First test the LCG it@#s self. This loop should
            // exercise any math errors.
            RNDuint t = new RNDuint();
            uint y;

            for( int i = 1; i <= 9999; i++)
                 y = t.n;

            if( t.n != successfulltest )
                return false;

            // Error throw testing
            try
            {
                t.n = m; // This MUST fail
            }
            catch( ArgumentOutOfRangeException e)
            {
                // So K
                return true;
            }
            catch
            {
                // Not So K
                return false;
            }

            return false; // if try did NOT generate an error
                          // then error detection is broken
        }                  

    }
}

原文转自:http://www.ltesting.net