Random.cs 9.1 KB

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