Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

259 lines
7.0 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose: Random number generator
  4. //
  5. // $Workfile: $
  6. // $NoKeywords: $
  7. //===========================================================================//
  8. #include "vstdlib/random.h"
  9. #include <math.h>
  10. #include "dbg.h"
  11. #include "tier0/memdbgon.h"
  12. #define IA 16807
  13. #define IM 2147483647
  14. #define IQ 127773
  15. #define IR 2836
  16. #define NDIV (1+(IM-1)/NTAB)
  17. #define MAX_RANDOM_RANGE 0x7FFFFFFFUL
  18. // fran1 -- return a random floating-point number on the interval [0,1)
  19. //
  20. #define AM (1.0/IM)
  21. #define EPS 1.2e-7
  22. #define RNMX (1.0-EPS)
  23. //-----------------------------------------------------------------------------
  24. // globals
  25. //-----------------------------------------------------------------------------
  26. static CUniformRandomStream s_UniformStream;
  27. static CGaussianRandomStream s_GaussianStream;
  28. static IUniformRandomStream *s_pUniformStream = &s_UniformStream;
  29. //-----------------------------------------------------------------------------
  30. // Installs a global random number generator, which will affect the Random functions above
  31. //-----------------------------------------------------------------------------
  32. void InstallUniformRandomStream( IUniformRandomStream *pStream )
  33. {
  34. s_pUniformStream = pStream ? pStream : &s_UniformStream;
  35. }
  36. //-----------------------------------------------------------------------------
  37. // A couple of convenience functions to access the library's global uniform stream
  38. //-----------------------------------------------------------------------------
  39. void RandomSeed( int iSeed )
  40. {
  41. s_pUniformStream->SetSeed( iSeed );
  42. }
  43. float RandomFloat( float flMinVal, float flMaxVal )
  44. {
  45. return s_pUniformStream->RandomFloat( flMinVal, flMaxVal );
  46. }
  47. float RandomFloatExp( float flMinVal, float flMaxVal, float flExponent )
  48. {
  49. return s_pUniformStream->RandomFloatExp( flMinVal, flMaxVal, flExponent );
  50. }
  51. int RandomInt( int iMinVal, int iMaxVal )
  52. {
  53. return s_pUniformStream->RandomInt( iMinVal, iMaxVal );
  54. }
  55. float RandomGaussianFloat( float flMean, float flStdDev )
  56. {
  57. return s_GaussianStream.RandomFloat( flMean, flStdDev );
  58. }
  59. //-----------------------------------------------------------------------------
  60. //
  61. // Implementation of the uniform random number stream
  62. //
  63. //-----------------------------------------------------------------------------
  64. CUniformRandomStream::CUniformRandomStream()
  65. {
  66. SetSeed(0);
  67. }
  68. void CUniformRandomStream::SetSeed( int iSeed )
  69. {
  70. AUTO_LOCK( m_mutex );
  71. m_idum = ( ( iSeed < 0 ) ? iSeed : -iSeed );
  72. m_iy = 0;
  73. }
  74. int CUniformRandomStream::GenerateRandomNumber()
  75. {
  76. AUTO_LOCK( m_mutex );
  77. int j;
  78. int k;
  79. if (m_idum <= 0 || !m_iy)
  80. {
  81. if (-(m_idum) < 1)
  82. m_idum=1;
  83. else
  84. m_idum = -(m_idum);
  85. for ( j=NTAB+7; j>=0; j--)
  86. {
  87. k = (m_idum)/IQ;
  88. m_idum = IA*(m_idum-k*IQ)-IR*k;
  89. if (m_idum < 0)
  90. m_idum += IM;
  91. if (j < NTAB)
  92. m_iv[j] = m_idum;
  93. }
  94. m_iy=m_iv[0];
  95. }
  96. k=(m_idum)/IQ;
  97. m_idum=IA*(m_idum-k*IQ)-IR*k;
  98. if (m_idum < 0)
  99. m_idum += IM;
  100. j=m_iy/NDIV;
  101. // We're seeing some strange memory corruption in the contents of s_pUniformStream.
  102. // Perhaps it's being caused by something writing past the end of this array?
  103. // Bounds-check in release to see if that's the case.
  104. if (j >= NTAB || j < 0)
  105. {
  106. DebuggerBreakIfDebugging();
  107. Warning("CUniformRandomStream had an array overrun: tried to write to element %d of 0..31. Contact Tom or Elan.\n", j);
  108. // Ensure that NTAB is a power of two.
  109. COMPILE_TIME_ASSERT( ( NTAB & ( NTAB - 1 ) ) == 0 );
  110. // Clamp j.
  111. j &= NTAB - 1;
  112. }
  113. m_iy=m_iv[j];
  114. m_iv[j] = m_idum;
  115. return m_iy;
  116. }
  117. float CUniformRandomStream::RandomFloat( float flLow, float flHigh )
  118. {
  119. // float in [0,1)
  120. float fl = AM * GenerateRandomNumber();
  121. if (fl > RNMX)
  122. {
  123. fl = RNMX;
  124. }
  125. return (fl * ( flHigh - flLow ) ) + flLow; // float in [low,high)
  126. }
  127. float CUniformRandomStream::RandomFloatExp( float flMinVal, float flMaxVal, float flExponent )
  128. {
  129. // float in [0,1)
  130. float fl = AM * GenerateRandomNumber();
  131. if (fl > RNMX)
  132. {
  133. fl = RNMX;
  134. }
  135. if ( flExponent != 1.0f )
  136. {
  137. fl = powf( fl, flExponent );
  138. }
  139. return (fl * ( flMaxVal - flMinVal ) ) + flMinVal; // float in [low,high)
  140. }
  141. int CUniformRandomStream::RandomInt( int iLow, int iHigh )
  142. {
  143. //ASSERT(lLow <= lHigh);
  144. unsigned int maxAcceptable;
  145. unsigned int x = iHigh-iLow+1;
  146. unsigned int n;
  147. if (x <= 1 || MAX_RANDOM_RANGE < x-1)
  148. {
  149. return iLow;
  150. }
  151. // The following maps a uniform distribution on the interval [0,MAX_RANDOM_RANGE]
  152. // to a smaller, client-specified range of [0,x-1] in a way that doesn't bias
  153. // the uniform distribution unfavorably. Even for a worst case x, the loop is
  154. // guaranteed to be taken no more than half the time, so for that worst case x,
  155. // the average number of times through the loop is 2. For cases where x is
  156. // much smaller than MAX_RANDOM_RANGE, the average number of times through the
  157. // loop is very close to 1.
  158. //
  159. maxAcceptable = MAX_RANDOM_RANGE - ((MAX_RANDOM_RANGE+1) % x );
  160. do
  161. {
  162. n = GenerateRandomNumber();
  163. } while (n > maxAcceptable);
  164. return iLow + (n % x);
  165. }
  166. //-----------------------------------------------------------------------------
  167. //
  168. // Implementation of the gaussian random number stream
  169. // We're gonna use the Box-Muller method (which actually generates 2
  170. // gaussian-distributed numbers at once)
  171. //
  172. //-----------------------------------------------------------------------------
  173. CGaussianRandomStream::CGaussianRandomStream( IUniformRandomStream *pUniformStream )
  174. {
  175. AttachToStream( pUniformStream );
  176. }
  177. //-----------------------------------------------------------------------------
  178. // Attaches to a random uniform stream
  179. //-----------------------------------------------------------------------------
  180. void CGaussianRandomStream::AttachToStream( IUniformRandomStream *pUniformStream )
  181. {
  182. AUTO_LOCK( m_mutex );
  183. m_pUniformStream = pUniformStream;
  184. m_bHaveValue = false;
  185. }
  186. //-----------------------------------------------------------------------------
  187. // Generates random numbers
  188. //-----------------------------------------------------------------------------
  189. float CGaussianRandomStream::RandomFloat( float flMean, float flStdDev )
  190. {
  191. AUTO_LOCK( m_mutex );
  192. IUniformRandomStream *pUniformStream = m_pUniformStream ? m_pUniformStream : s_pUniformStream;
  193. float fac,rsq,v1,v2;
  194. if (!m_bHaveValue)
  195. {
  196. // Pick 2 random #s from -1 to 1
  197. // Make sure they lie inside the unit circle. If they don't, try again
  198. do
  199. {
  200. v1 = 2.0f * pUniformStream->RandomFloat() - 1.0f;
  201. v2 = 2.0f * pUniformStream->RandomFloat() - 1.0f;
  202. rsq = v1*v1 + v2*v2;
  203. } while ((rsq > 1.0f) || (rsq == 0.0f));
  204. // The box-muller transformation to get the two gaussian numbers
  205. fac = sqrtf( -2.0f * log(rsq) / rsq );
  206. // Store off one value for later use
  207. m_flRandomValue = v1 * fac;
  208. m_bHaveValue = true;
  209. return flStdDev * (v2 * fac) + flMean;
  210. }
  211. else
  212. {
  213. m_bHaveValue = false;
  214. return flStdDev * m_flRandomValue + flMean;
  215. }
  216. }
  217. //-----------------------------------------------------------------------------
  218. // Creates a histogram (for testing)
  219. //-----------------------------------------------------------------------------