From f7f21e2b8cc9ccb36ba8b1d2e3604d3672bc7d17 Mon Sep 17 00:00:00 2001 From: Glenn Maynard Date: Wed, 18 Oct 2006 02:28:06 +0000 Subject: [PATCH] Replace PRNG with a standard MT. RandomGen r; r(); now generates [0,2^31-1] on 32-bit platforms, not [0,2^31-2]. No longer uses MAX_INT, so the default behavior doesn't depend on the size of an int. RandomInt no longer generates a float and truncates it. That limited the values that could be returned to those that fit in a 32-bit float. Remove NR code. It's not free. This code is not threadsafe. Locking access would kill performance. The old code wasn't, either. I think most symptoms would be innocuous enough to not worry about. --- stepmania/src/RageUtil.cpp | 88 ++++++++++++++++++++++++-------------- stepmania/src/RageUtil.h | 42 ++++++++++++------ 2 files changed, 85 insertions(+), 45 deletions(-) diff --git a/stepmania/src/RageUtil.cpp b/stepmania/src/RageUtil.cpp index 7edcb365eb..fabdca125a 100644 --- a/stepmania/src/RageUtil.cpp +++ b/stepmania/src/RageUtil.cpp @@ -15,51 +15,75 @@ #include #include -int randseed = time(NULL); +RandomGen g_RandomNumberGenerator; -// From "Numerical Recipes in C". -static float RandomFloatFromSeed( int &seed ) +MersenneTwister::MersenneTwister( int iSeed ) { - const int MASK = 123459876; - seed ^= MASK; - - const int IA = 16807; - const int IM = 2147483647; - - const int IQ = 127773; - const int IR = 2836; - - long k = seed / IQ; - seed = IA*(seed-k*IQ)-IR*k; - if( seed < 0 ) - seed += IM; - - const float AM = .999999f / IM; - float ans = AM * seed; - - seed ^= MASK; - return ans; + Reset( iSeed ); } -float RandomFloat() +void MersenneTwister::Reset( int iSeed ) { - return RandomFloatFromSeed( randseed ); + if( iSeed == 0 ) + iSeed = time(NULL); + + m_Values[0] = iSeed; + m_iNext = 0; + for( int i = 1; i < 624; ++i ) + m_Values[i] = ((69069 * m_Values[i-1]) + 1) & 0xFFFFFFFF; + + GenerateValues(); } -RandomGen::RandomGen( unsigned long seed_ ) +void MersenneTwister::GenerateValues() { - seed = seed_; - if(seed == 0) - seed = time(NULL); + static const int mask[] = { 0, 0x9908B0DF }; + + for( int i = 0; i < 227; ++i ) + { + int iVal = (m_Values[i] & 0x80000000) | (m_Values[i+1] & 0x7FFFFFFF); + int iNext = (i + 397); + + m_Values[i] = m_Values[iNext]; + m_Values[i] ^= (iVal >> 1); + m_Values[i] ^= mask[iVal&1]; + } + + for( int i = 227; i < 623; ++i ) + { + int iVal = (m_Values[i] & 0x80000000) | (m_Values[i+1] & 0x7FFFFFFF); + int iNext = (i + 397) - 624; + + m_Values[i] = m_Values[iNext]; + m_Values[i] ^= (iVal >> 1); + m_Values[i] ^= mask[iVal&1]; + } + + int iVal = (m_Values[623] & 0x80000000) + (m_Values[0] & 0x7FFFFFFF); + int iNext = (623 + 397) - 624; + m_Values[623] = m_Values[iNext] ^ (iVal>>1); + m_Values[623] ^= mask[iVal&1]; } -int RandomGen::operator() ( int n ) +int MersenneTwister::Temper( int iVal ) { - float f = RandomFloatFromSeed(seed) * (n); - int ans = int(f); - return ans; + iVal ^= (iVal >> 11); + iVal ^= (iVal << 7) & 0x9D2C5680; + iVal ^= (iVal << 15) & 0xEFC60000; + iVal ^= (iVal >> 18); + return iVal; } +int MersenneTwister::operator()() +{ + if( m_iNext == 624 ) + { + m_iNext = 0; + GenerateValues(); + } + + return Temper( m_Values[m_iNext++] ); +} void fapproach( float& val, float other_val, float to_move ) { diff --git a/stepmania/src/RageUtil.h b/stepmania/src/RageUtil.h index 4e885d3e22..a8f50fd8d4 100644 --- a/stepmania/src/RageUtil.h +++ b/stepmania/src/RageUtil.h @@ -227,8 +227,34 @@ inline uint32_t Swap24LE( uint32_t n ) { return Swap24( n ); } inline uint16_t Swap16LE( uint16_t n ) { return Swap16( n ); } #endif +struct MersenneTwister +{ + MersenneTwister( int iSeed = 0 ); // 0 = time() + int operator()(); // returns [0,2^31-1] + int operator()( int n ) // returns [0,n) + { + return (*this)() % n; + } + + void Reset( int iSeed ); + +private: + static int Temper( int iValue ); + void GenerateValues(); + + int m_Values[624]; + int m_iNext; +}; +typedef MersenneTwister RandomGen; + +extern RandomGen g_RandomNumberGenerator; + // [0.0f,1.0f) -float RandomFloat(); +inline float RandomFloat() +{ + return g_RandomNumberGenerator() / 2147483648.0f; +} + // Returns a float between dLow and dHigh inclusive inline float RandomFloat( float fLow, float fHigh ) @@ -239,25 +265,15 @@ inline float RandomFloat( float fLow, float fHigh ) // Returns an integer between nLow and nHigh inclusive inline int RandomInt( int nLow, int nHigh ) { - return int( RandomFloat() * (nHigh - nLow + 1) + nLow ); + return int( g_RandomNumberGenerator(nHigh - nLow + 1) + nLow ); } // Returns an integer between 0 and n-1 inclusive (replacement for rand() % n). inline int RandomInt( int n ) { - return RandomInt( 0, n-1 ); + return int( g_RandomNumberGenerator(n) ); } -/* Alternative: */ -class RandomGen -{ - int seed; - -public: - RandomGen( unsigned long seed = 0 ); - int operator() ( int n = INT_MAX-1 ); // return number [0,n) -}; - // Simple function for generating random numbers inline float randomf( const float low=-1.0f, const float high=1.0f )