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 ...)
///
/// Adv
antages 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 su
clearcase/" 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