| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- // Licensed to the .NET Foundation under one or more agreements.
- // The .NET Foundation licenses this file to you under the MIT license.
- // See the LICENSE file in the project root for more information.
- namespace System
- {
- public class Random
- {
- //
- // Private Constants
- //
- private const int MBIG = int.MaxValue;
- private const int MSEED = 161803398;
- private const int MZ = 0;
- //
- // Member Variables
- //
- private int _inext;
- private int _inextp;
- private int[] _seedArray = new int[56];
- //
- // Public Constants
- //
- //
- // Native Declarations
- //
- //
- // Constructors
- //
- /*=========================================================================================
- **Action: Initializes a new instance of the Random class, using a default seed value
- ===========================================================================================*/
- public Random()
- : this(GenerateSeed())
- {
- }
- /*=========================================================================================
- **Action: Initializes a new instance of the Random class, using a specified seed value
- ===========================================================================================*/
- public Random(int Seed)
- {
- int ii = 0;
- int mj, mk;
- //Initialize our Seed array.
- int subtraction = (Seed == int.MinValue) ? int.MaxValue : Math.Abs(Seed);
- mj = MSEED - subtraction;
- _seedArray[55] = mj;
- mk = 1;
- for (int i = 1; i < 55; i++)
- { //Apparently the range [1..55] is special (Knuth) and so we're wasting the 0'th position.
- if ((ii += 21) >= 55) ii -= 55;
- _seedArray[ii] = mk;
- mk = mj - mk;
- if (mk < 0) mk += MBIG;
- mj = _seedArray[ii];
- }
- for (int k = 1; k < 5; k++)
- {
- for (int i = 1; i < 56; i++)
- {
- int n = i + 30;
- if (n >= 55) n -= 55;
- _seedArray[i] -= _seedArray[1 + n];
- if (_seedArray[i] < 0) _seedArray[i] += MBIG;
- }
- }
- _inext = 0;
- _inextp = 21;
- Seed = 1;
- }
- //
- // Package Private Methods
- //
- /*====================================Sample====================================
- **Action: Return a new random number [0..1) and reSeed the Seed array.
- **Returns: A double [0..1)
- **Arguments: None
- **Exceptions: None
- ==============================================================================*/
- protected virtual double Sample()
- {
- //Including this division at the end gives us significantly improved
- //random number distribution.
- return (InternalSample() * (1.0 / MBIG));
- }
- private int InternalSample()
- {
- int retVal;
- int locINext = _inext;
- int locINextp = _inextp;
- if (++locINext >= 56) locINext = 1;
- if (++locINextp >= 56) locINextp = 1;
- retVal = _seedArray[locINext] - _seedArray[locINextp];
- if (retVal == MBIG) retVal--;
- if (retVal < 0) retVal += MBIG;
- _seedArray[locINext] = retVal;
- _inext = locINext;
- _inextp = locINextp;
- return retVal;
- }
- [ThreadStatic]
- private static Random t_threadRandom;
- private static readonly Random s_globalRandom = new Random(GenerateGlobalSeed());
- /*=====================================GenerateSeed=====================================
- **Returns: An integer that can be used as seed values for consecutively
- creating lots of instances on the same thread within a short period of time.
- ========================================================================================*/
- private static int GenerateSeed()
- {
- Random rnd = t_threadRandom;
- if (rnd == null)
- {
- int seed;
- lock (s_globalRandom)
- {
- seed = s_globalRandom.Next();
- }
- rnd = new Random(seed);
- t_threadRandom = rnd;
- }
- return rnd.Next();
- }
- /*==================================GenerateGlobalSeed====================================
- **Action: Creates a number to use as global seed.
- **Returns: An integer that is safe to use as seed values for thread-local seed generators.
- ==========================================================================================*/
- private static unsafe int GenerateGlobalSeed()
- {
- int result;
- Interop.GetRandomBytes((byte*)&result, sizeof(int));
- return result;
- }
- //
- // Public Instance Methods
- //
- /*=====================================Next=====================================
- **Returns: An int [0..int.MaxValue)
- **Arguments: None
- **Exceptions: None.
- ==============================================================================*/
- public virtual int Next()
- {
- return InternalSample();
- }
- private double GetSampleForLargeRange()
- {
- // The distribution of double value returned by Sample
- // is not distributed well enough for a large range.
- // If we use Sample for a range [int.MinValue..int.MaxValue)
- // We will end up getting even numbers only.
- int result = InternalSample();
- // Note we can't use addition here. The distribution will be bad if we do that.
- bool negative = (InternalSample() % 2 == 0) ? true : false; // decide the sign based on second sample
- if (negative)
- {
- result = -result;
- }
- double d = result;
- d += (int.MaxValue - 1); // get a number in range [0 .. 2 * Int32MaxValue - 1)
- d /= 2 * (uint)int.MaxValue - 1;
- return d;
- }
- /*=====================================Next=====================================
- **Returns: An int [minvalue..maxvalue)
- **Arguments: minValue -- the least legal value for the Random number.
- ** maxValue -- One greater than the greatest legal return value.
- **Exceptions: None.
- ==============================================================================*/
- public virtual int Next(int minValue, int maxValue)
- {
- if (minValue > maxValue)
- {
- throw new ArgumentOutOfRangeException(nameof(minValue), SR.Format(SR.Argument_MinMaxValue, nameof(minValue), nameof(maxValue)));
- }
- long range = (long)maxValue - minValue;
- if (range <= int.MaxValue)
- {
- return ((int)(Sample() * range) + minValue);
- }
- else
- {
- return (int)((long)(GetSampleForLargeRange() * range) + minValue);
- }
- }
- /*=====================================Next=====================================
- **Returns: An int [0..maxValue)
- **Arguments: maxValue -- One more than the greatest legal return value.
- **Exceptions: None.
- ==============================================================================*/
- public virtual int Next(int maxValue)
- {
- if (maxValue < 0)
- {
- throw new ArgumentOutOfRangeException(nameof(maxValue), SR.Format(SR.ArgumentOutOfRange_MustBePositive, nameof(maxValue)));
- }
- return (int)(Sample() * maxValue);
- }
- /*=====================================Next=====================================
- **Returns: A double [0..1)
- **Arguments: None
- **Exceptions: None
- ==============================================================================*/
- public virtual double NextDouble()
- {
- return Sample();
- }
- /*==================================NextBytes===================================
- **Action: Fills the byte array with random bytes [0..0x7f]. The entire array is filled.
- **Returns:Void
- **Arguments: buffer -- the array to be filled.
- **Exceptions: None
- ==============================================================================*/
- public virtual void NextBytes(byte[] buffer)
- {
- if (buffer == null) throw new ArgumentNullException(nameof(buffer));
- for (int i = 0; i < buffer.Length; i++)
- {
- buffer[i] = (byte)InternalSample();
- }
- }
- public virtual void NextBytes(Span<byte> buffer)
- {
- for (int i = 0; i < buffer.Length; i++)
- {
- buffer[i] = (byte)Next();
- }
- }
- }
- }
|