Random.cs 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. // Licensed to the .NET Foundation under one or more agreements.
  2. // The .NET Foundation licenses this file to you under the MIT license.
  3. // See the LICENSE file in the project root for more information.
  4. namespace System
  5. {
  6. public class Random
  7. {
  8. //
  9. // Private Constants
  10. //
  11. private const int MBIG = int.MaxValue;
  12. private const int MSEED = 161803398;
  13. private const int MZ = 0;
  14. //
  15. // Member Variables
  16. //
  17. private int _inext;
  18. private int _inextp;
  19. private int[] _seedArray = new int[56];
  20. //
  21. // Public Constants
  22. //
  23. //
  24. // Native Declarations
  25. //
  26. //
  27. // Constructors
  28. //
  29. /*=========================================================================================
  30. **Action: Initializes a new instance of the Random class, using a default seed value
  31. ===========================================================================================*/
  32. public Random()
  33. : this(GenerateSeed())
  34. {
  35. }
  36. /*=========================================================================================
  37. **Action: Initializes a new instance of the Random class, using a specified seed value
  38. ===========================================================================================*/
  39. public Random(int Seed)
  40. {
  41. int ii = 0;
  42. int mj, mk;
  43. //Initialize our Seed array.
  44. int subtraction = (Seed == int.MinValue) ? int.MaxValue : Math.Abs(Seed);
  45. mj = MSEED - subtraction;
  46. _seedArray[55] = mj;
  47. mk = 1;
  48. for (int i = 1; i < 55; i++)
  49. { //Apparently the range [1..55] is special (Knuth) and so we're wasting the 0'th position.
  50. if ((ii += 21) >= 55) ii -= 55;
  51. _seedArray[ii] = mk;
  52. mk = mj - mk;
  53. if (mk < 0) mk += MBIG;
  54. mj = _seedArray[ii];
  55. }
  56. for (int k = 1; k < 5; k++)
  57. {
  58. for (int i = 1; i < 56; i++)
  59. {
  60. int n = i + 30;
  61. if (n >= 55) n -= 55;
  62. _seedArray[i] -= _seedArray[1 + n];
  63. if (_seedArray[i] < 0) _seedArray[i] += MBIG;
  64. }
  65. }
  66. _inext = 0;
  67. _inextp = 21;
  68. Seed = 1;
  69. }
  70. //
  71. // Package Private Methods
  72. //
  73. /*====================================Sample====================================
  74. **Action: Return a new random number [0..1) and reSeed the Seed array.
  75. **Returns: A double [0..1)
  76. **Arguments: None
  77. **Exceptions: None
  78. ==============================================================================*/
  79. protected virtual double Sample()
  80. {
  81. //Including this division at the end gives us significantly improved
  82. //random number distribution.
  83. return (InternalSample() * (1.0 / MBIG));
  84. }
  85. private int InternalSample()
  86. {
  87. int retVal;
  88. int locINext = _inext;
  89. int locINextp = _inextp;
  90. if (++locINext >= 56) locINext = 1;
  91. if (++locINextp >= 56) locINextp = 1;
  92. retVal = _seedArray[locINext] - _seedArray[locINextp];
  93. if (retVal == MBIG) retVal--;
  94. if (retVal < 0) retVal += MBIG;
  95. _seedArray[locINext] = retVal;
  96. _inext = locINext;
  97. _inextp = locINextp;
  98. return retVal;
  99. }
  100. [ThreadStatic]
  101. private static Random t_threadRandom;
  102. private static readonly Random s_globalRandom = new Random(GenerateGlobalSeed());
  103. /*=====================================GenerateSeed=====================================
  104. **Returns: An integer that can be used as seed values for consecutively
  105. creating lots of instances on the same thread within a short period of time.
  106. ========================================================================================*/
  107. private static int GenerateSeed()
  108. {
  109. Random rnd = t_threadRandom;
  110. if (rnd == null)
  111. {
  112. int seed;
  113. lock (s_globalRandom)
  114. {
  115. seed = s_globalRandom.Next();
  116. }
  117. rnd = new Random(seed);
  118. t_threadRandom = rnd;
  119. }
  120. return rnd.Next();
  121. }
  122. /*==================================GenerateGlobalSeed====================================
  123. **Action: Creates a number to use as global seed.
  124. **Returns: An integer that is safe to use as seed values for thread-local seed generators.
  125. ==========================================================================================*/
  126. private static unsafe int GenerateGlobalSeed()
  127. {
  128. int result;
  129. Interop.GetRandomBytes((byte*)&result, sizeof(int));
  130. return result;
  131. }
  132. //
  133. // Public Instance Methods
  134. //
  135. /*=====================================Next=====================================
  136. **Returns: An int [0..int.MaxValue)
  137. **Arguments: None
  138. **Exceptions: None.
  139. ==============================================================================*/
  140. public virtual int Next()
  141. {
  142. return InternalSample();
  143. }
  144. private double GetSampleForLargeRange()
  145. {
  146. // The distribution of double value returned by Sample
  147. // is not distributed well enough for a large range.
  148. // If we use Sample for a range [int.MinValue..int.MaxValue)
  149. // We will end up getting even numbers only.
  150. int result = InternalSample();
  151. // Note we can't use addition here. The distribution will be bad if we do that.
  152. bool negative = (InternalSample() % 2 == 0) ? true : false; // decide the sign based on second sample
  153. if (negative)
  154. {
  155. result = -result;
  156. }
  157. double d = result;
  158. d += (int.MaxValue - 1); // get a number in range [0 .. 2 * Int32MaxValue - 1)
  159. d /= 2 * (uint)int.MaxValue - 1;
  160. return d;
  161. }
  162. /*=====================================Next=====================================
  163. **Returns: An int [minvalue..maxvalue)
  164. **Arguments: minValue -- the least legal value for the Random number.
  165. ** maxValue -- One greater than the greatest legal return value.
  166. **Exceptions: None.
  167. ==============================================================================*/
  168. public virtual int Next(int minValue, int maxValue)
  169. {
  170. if (minValue > maxValue)
  171. {
  172. throw new ArgumentOutOfRangeException(nameof(minValue), SR.Format(SR.Argument_MinMaxValue, nameof(minValue), nameof(maxValue)));
  173. }
  174. long range = (long)maxValue - minValue;
  175. if (range <= int.MaxValue)
  176. {
  177. return ((int)(Sample() * range) + minValue);
  178. }
  179. else
  180. {
  181. return (int)((long)(GetSampleForLargeRange() * range) + minValue);
  182. }
  183. }
  184. /*=====================================Next=====================================
  185. **Returns: An int [0..maxValue)
  186. **Arguments: maxValue -- One more than the greatest legal return value.
  187. **Exceptions: None.
  188. ==============================================================================*/
  189. public virtual int Next(int maxValue)
  190. {
  191. if (maxValue < 0)
  192. {
  193. throw new ArgumentOutOfRangeException(nameof(maxValue), SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(maxValue)));
  194. }
  195. return (int)(Sample() * maxValue);
  196. }
  197. /*=====================================Next=====================================
  198. **Returns: A double [0..1)
  199. **Arguments: None
  200. **Exceptions: None
  201. ==============================================================================*/
  202. public virtual double NextDouble()
  203. {
  204. return Sample();
  205. }
  206. /*==================================NextBytes===================================
  207. **Action: Fills the byte array with random bytes [0..0x7f]. The entire array is filled.
  208. **Returns:Void
  209. **Arguments: buffer -- the array to be filled.
  210. **Exceptions: None
  211. ==============================================================================*/
  212. public virtual void NextBytes(byte[] buffer)
  213. {
  214. if (buffer == null) throw new ArgumentNullException(nameof(buffer));
  215. for (int i = 0; i < buffer.Length; i++)
  216. {
  217. buffer[i] = (byte)InternalSample();
  218. }
  219. }
  220. public virtual void NextBytes(Span<byte> buffer)
  221. {
  222. for (int i = 0; i < buffer.Length; i++)
  223. {
  224. buffer[i] = (byte)Next();
  225. }
  226. }
  227. }
  228. }