Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Will System.Random always generate predictable numbers for a given seed across platforms?

Tags:

c#

.net

random

I know that Object.GetHashCode can return different values for the same object (an identical string, for example) depending on the platform. I thus can't rely on this for a cross-platform simple hash.

But can I rely on System.Random? Will it produce the same results for a given seed regardless of whether or not I'm on Microsoft .NET / Mono / x86 / x64, etc.?

like image 979
vargonian Avatar asked Mar 16 '17 00:03

vargonian


People also ask

How are random numbers generated from a seed?

Random seeds are often generated from the state of the computer system (such as the time), a cryptographically secure pseudorandom number generator or from a hardware random number generator.

Why is a really good random number generator needed for seed generation?

This is because given a starting value (===> the seed) they will always provide the same sequence of pseudo random results. A good generator will produce a sequence that can not be distinguished - in statistical terms - from a true random sequence (throw a true die, true coin, etc).

Is random seed same on all machines?

Seed function is used to save the state of a random function, so that it can generate same random numbers on multiple executions of the code on the same machine or on different machines (for a specific seed value).

What is random seed in random number generator?

A random seed is a starting point in generating random numbers. A random seed specifies the start point when a computer generates a random number sequence. This can be any number, but it usually comes from seconds on a computer system's clock (Henkemans & Lee, 2001).


2 Answers

As Jeremy mentioned in his answer the documentation states that the number generator is not guaranteed consistent across .NET versions.

However, the documentation also tells you how to implement your algorithm too.

You can implement your own random number generator by inheriting from the Random class and supplying your random number generation algorithm. To supply your own algorithm, you must override the Sample method, which implements the random number generation algorithm. You should also override the Next(), Next(Int32, Int32), and NextBytes methods to ensure that they call your overridden Sample method. You don't have to override the Next(Int32) and NextDouble methods.

Using that we can make our own random class that uses a known fixed algorithm you can use. For example, we could go to the .Net source and implement the current random algorithm, this would allow us to use the ConsistantRandom class and know for sure the algorithom won't change on us.

The following example is derived from the .NET Core 1.1.0 source.

using System;

namespace ConsoleApplication1
{
    public class ConsistantRandom: Random
    {
        private const int MBIG = Int32.MaxValue;
        private const int MSEED = 161803398;
        private const int MZ = 0;

        private int inext;
        private int inextp;
        private int[] SeedArray = new int[56];

        public ConsistantRandom()
            : this(Environment.TickCount)
        {
        }

        public ConsistantRandom(int seed)
        {
            int ii;
            int mj, mk;

            int subtraction = (seed == Int32.MinValue) ? Int32.MaxValue : Math.Abs(seed);
            mj = MSEED - subtraction;
            SeedArray[55] = mj;
            mk = 1;
            for (int i = 1; i < 55; i++)
            {
                ii = (21 * i) % 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++)
                {
                    SeedArray[i] -= SeedArray[1 + (i + 30) % 55];
                    if (SeedArray[i] < 0) SeedArray[i] += MBIG;
                }
            }
            inext = 0;
            inextp = 21;
        }
        protected override double Sample()
        {
            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;
        }

        public override int Next()
        {
            return InternalSample();
        }

        private double GetSampleForLargeRange()
        {
            int result = InternalSample();
            bool negative = (InternalSample() % 2 == 0) ? true : false;
            if (negative)
            {
                result = -result;
            }
            double d = result;
            d += (Int32.MaxValue - 1);
            d /= 2 * (uint)Int32.MaxValue - 1;
            return d;
        }


        public override int Next(int minValue, int maxValue)
        {
            if (minValue > maxValue)
            {
                throw new ArgumentOutOfRangeException("minValue");
            }

            long range = (long)maxValue - minValue;
            if (range <= (long)Int32.MaxValue)
            {
                return ((int)(Sample() * range) + minValue);
            }
            else
            {
                return (int)((long)(GetSampleForLargeRange() * range) + minValue);
            }
        }
        public override void NextBytes(byte[] buffer)
        {
            if (buffer == null) throw new ArgumentNullException("buffer");
            for (int i = 0; i < buffer.Length; i++)
            {
                buffer[i] = (byte)(InternalSample() % (Byte.MaxValue + 1));
            }
        }
    }
    
}

Because ConsistantRandom derives from Random you can use the class as a drop in replacement anywhere you had the type Random before, you just need to replace Random rnd = new Random(yourSeed); with Random rnd = new ConsistantRandom(yourSeed);.

like image 165
Scott Chamberlain Avatar answered Oct 21 '22 21:10

Scott Chamberlain


No, you can not rely on consistent values being generated across platforms or versions. From the .NET Framework docs for System.Random (the less-detailed .NET Core docs don't address this):

The implementation of the random number generator in the Random class isn't guaranteed to remain the same across major versions of the .NET Framework. As a result, you shouldn't assume that the same seed will result in the same pseudo-random sequence in different versions of the .NET Framework.

like image 7
Jeremy Avatar answered Oct 21 '22 20:10

Jeremy