top of page
Search
cmcrter

Random Number Generation

Every procedural algorithm has steps wherein they need choose a random number with certain parameters. Unfortunately, computers are logic based machines built upon structure and certainty, so having truly random numbers on them are impossible. However some mathematical process can appear seemingly random called "Pseudo Random Number Generators".


Within the project the Wave Function Collapse process will need to choose random numbers at multiple steps. Since the idea is to have the project within the game engine Unity, I could just use their Random.Range function.


But how "random" is it?


I decided to test it alongside Mersenne Twister + XORShift, a different XORShift and Microsoft's version of Linear Congruential Generator. From my testing, it seems like XORShift and LCG are the fastest of the 4, whereas the Unity.Random and Mersenne Twister are slower but more evenly distributed. The code snippets shown below were used to create the above gif, with help from various resources online. The algorithms once understood can be combined and used to add another layer of randomness onto it.

namespace WFC.Rand
{
    [Serializable]
    //This will be the default class I use to return numbers
    public class Rand
    {
        #region Variables

        //The seed is set with a default
        protected int seed= 133321;

        #endregion

        #region Constructors

        public Rand()
        {

        }

        //Generally I want a seed
        public Rand(int newSeed)
        {
            seed = newSeed;
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Setting the seed to retrieve a random number from
        /// </summary>
        public void SetSeed(int newSeed)
        {
            seed = newSeed;
        }

        /// <summary>
        /// Gets a random number
        /// </summary>
        public int ReturnRandom()
        {
            return Noise();
        }

        /// <summary>
        /// Gets a random number within a given maximum
        /// </summary>
        public int ReturnRandom(int MaxNumber)
        {
            return Noise() % MaxNumber;
        }

        #endregion

        #region Protected Methods

        //The mathematical function to return the random number
        protected virtual int Noise()
        {
            return seed;
        }

        #endregion
    }

    public class Mersenne_Twister : Rand
    {
        //State Size
        const int Size = 624;
        //Shift Size
        const int Period = 397;
        //Difference
        const int diff = Size - Period;
        //Mask Bits
        const int MaskSize = 30;
        //XORMask
        const UInt32 BitMask = 0x9908b0df;

        /// internal state
        UInt32[] State = new UInt32[Size];
        int next;

        public Mersenne_Twister()
        {
            //Setting the values up for the first states
            State[0] = (UInt32)seed;
            for(int i = 1; i < Size; i++)
            {
                UInt32 v = (uint)(1812433253UL * (State[i - 1] ^ (State[i - 1] >> MaskSize)));
                State[i] = (uint)(v + i);
            }

            //Starting the chain
            Twist();
        }

        public Mersenne_Twister(int seed)
        {
            State[0] = (UInt32)seed;
            for (int i = 1; i < Size; i++)
            {
                UInt32 v = (uint)(1812433253UL * (State [i - 1] ^ (State [i - 1] >> MaskSize)));
                State [i] = (uint)(v + i);
            }

            //Starting the chain
            Twist();
        }

        //The mathematical function to return the random number
        protected override int Noise()
        {
            //Is it necessary to twist?
            if (next >= Size)
            {
                Twist();
            }

            //Get next state
            UInt32 x = State[next++];

            //Doing a small XORShift on the state to make it seem more random
            x ^= x >> 11;
            x ^= ( x << 7 ) & 0x9d2c5680;
            x ^= ( x << 15 ) & 0xefc60000;
            x ^= x >> 18;

            return (int)x;
        }

        //Computing the states again
        private void Twist()
        {
            int i = 0;
            UInt32 current;

            //The first half of states
            for (i = 0; i < diff; i++)
            {
                current = ((State [i] & 0x80000000) | (State[i + 1] & 0x7fffffff));
                State[i] = (State[i + Period] ^ (current >> 1) ^ ((current & 1) * BitMask));
            }

            //Remaining States
            for (int j = i; j < Size - 1; j++)
            {
                current = (State[j] & 0x80000000) | (State[j + 1] & 0x7fffffff);
                State[j] = (State [j - diff] ^ ( current >> 1 ) ^ ( ( current & 1 ) * BitMask));
            }

            i = State.Length - 1;

            //The last state is done seperately which will wrap around
            current = (State[i] & 0x80000000) | (State[0] & 0x7fffffff);
            State [i] = (State[Period - 1] ^ (current >> 1) ^ ((current & 1) * BitMask));

            //Starting the cycle again
            next = 0;
        }
    }

    //A simple XORShift class
    public class XORShift : Rand
    {
        int x = 123456789;
        int y = 362436069;
        int z = 521288629;
        int w = 88675123;
        int t;

        public XORShift()
        {
            
        }

        //The mathematical function to return the random number
        protected override int Noise()
        {
            //This is an XORShift in it's most basic form with a period of 2^128 -1
            t = x ^ ( x << 11 );
            x = y; y = z; z = w;
            
            return w = w ^ (w >> 19) ^ (t ^ (t >> 8));
        }
    }

    //A simple Linear Congruential Generator
    public class LCG : Rand
    {
        public LCG(int newSeed)
        {
            seed = newSeed;
        }

        protected override int Noise()
        {
            //This is the Microsoft way of doing the LCG
            return ((seed = 214013 * seed + 2531011) & int.MaxValue) >> 16;
        }
    } 
}

The XORShift itself currently does not use the seed, but that will change soon so the X is the seed. Here are the aforementioned references for each method of getting random numbers, as well as using the wikipedia page for each: XORShift, MersenneTwister, LCG




15 views0 comments

Recent Posts

See All

Comments


Commenting has been turned off.
Post: Blog2_Post
bottom of page