A Good 31 bit Random Number Generator Class - 中国WEB开发者网络 (http://www.webasp.net) -- 技术教程 (http://www.webasp.net/article/) --- A Good 31 bit Random Number Generator Class (http://www.webasp.net/article/5/4173.htm) |
| -- 作者:未知 -- 发布日期: 2003-07-12 |
| 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 successfully 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 } } } |
| webasp.net |