Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

How does UnityEngine.Random initialize the state parameters of Xorshift in Random.InitState?

Discussion in 'Scripting' started by Macklin, Jan 20, 2021.

  1. Macklin

    Macklin

    Joined:
    Jun 13, 2015
    Posts:
    11
    So we know that UnityEngine.Random uses the Xorshift algorithm to generate random values, using a state consisting of 4 32-bit unsigned integers (lets call them x, y, z, w - x being the seed). Can we get any insight as to how the initial yzw values of the state are calculated via InitState? For example:
    Random.InitState(1234)
    results in the following state (thanks to Bunny83):
    x = 1234, y = 3159640283, z = 3392860520, w = 3460949513


    I think I can safely assume that the initial state is somehow derived from the seed, but there could be a million different ways to do so - and the code that actually carries this out is in the C++ portion of the Unity source code - which is a bit of a bummer.

    Understandably this could have security implications (maybe), but realistically if a game had to worry about that they'd be hopefully using an actual cryptographic algorithm, or at least a different random number generator.

    I'm working on an external program that needs to replicate the behaviour of Unity's RNG and generate equivalent values using only the seed (i.e. without knowing the initial state in advance, and without transmitting it in any way). Here's an example assuming I do know the state in advance.

    Code (csharp):
    1. public void Test()
    2. {
    3.     var sb = new System.Text.StringBuilder();
    4.     UnityEngine.Random.InitState(1234);
    5.  
    6.     // Generate 5 values from each RNG
    7.     for (int i = 0; i < 5; i++)
    8.     {
    9.         int ur = UnityEngine.Random.Range(0, int.MaxValue);
    10.         int xr = (int)(Xorshift() % int.MaxValue);
    11.         sb.AppendLine(string.Format("#{0} - {1}\t{2}", i, ur.ToString().PadRight(12), xr.ToString().PadRight(12)));
    12.     }
    13.  
    14.     Debug.Log(sb);
    15. }
    16.  
    17. // The initial state if the seed is 1234
    18. private static uint x = 1234, y = 3159640283, z = 3392860520, w = 3460949513;
    19.  
    20. public static uint Xorshift()
    21. {
    22.     uint t = x ^ (x << 11);
    23.     x = y; y = z; z = w;
    24.     return w = w ^ (w >> 19) ^ t ^ (t >> 8);
    25. }
    Any help would be much appreciated!
     
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,749
    Correction: you posted a link to one random internet post from one random guy named "andeeeeee" who asserts this. He doesn't even seem to work for Unity. He posted that nine years ago (August 2012... that would have been roughly Unity version 3.5.5!) and we don't even know if it was correct that day, let alone if it is still correct now in Unity2020+, and certainly it gives us zero insight if this will still be true forever and ever.

    Looking at the official documentation for UnityEngine.Random, it looks like they specifically DO NOT tell you what the algorithm is. This lets them change it in the future. I am sure this is intentional.

    This makes no sense. Of course they are not: it's just an LFSR generator.

    If you need cryptographically-secure random numbers, then implement your own routine or else import a package that does it for you.
     
  3. PraetorBlue

    PraetorBlue

    Joined:
    Dec 13, 2012
    Posts:
    7,722
    Your best bet is to use a different random number generator that you either:
    1. Know the inner workings of and can replicate that in your other application
    2. Can simply use the same library/package in that other application
     
  4. Macklin

    Macklin

    Joined:
    Jun 13, 2015
    Posts:
    11
    2020 still uses Xorshift - the above code validates this, and robustness or future proofing doesn't matter to me since the application that I need to match against is a build (Unity 4.6 of all things!) that will not change. I understand that if I wanted control over these parameters, I could just do it myself and avoid asking in the first place - but that is not the goal.

    I didn't mean that Unity.Random was a cryptographic algorithm (of course it isn't). I meant if people were using Unity.Random for cryptographic applications they really shouldn't.
     
    Last edited: Jan 20, 2021
  5. MoatShrimp

    MoatShrimp

    Joined:
    Jan 29, 2021
    Posts:
    1
    Been trying to figure out how Unity is seeding xorshift this whole day and I've finally found it:

    The seeds from Random.InitState(0) are the following:
    s[0] = 0
    s[1] = 1
    s[2] = 1812433254
    s[3] = 1900727103

    With 1812433254 being the first interesting number.

    Searching for this number, we can see that MT19937 PRNG use 1812433253 as a constant in the formula:
    x = f * (x[i−1] ^ (x[i−1] >> (w -2))) + i
    Where 'f' = 1812433253 and 'w' is the number of bits in the unit x.

    With this info, and after a bit of trial and error I finally found that the pseudo-seeds are generated via a the pattern of:
    uint s= (uint)(1812433253 * s[i-1] + 1)
    using the overflow of the unsigned int to create pseudo-random seeds from your initial one.

    TLDR:

    Code (CSharp):
    1. uint x = seed;
    2. uint y = (uint)(1812433253 * x + 1);
    3. uint z = (uint)(1812433253 * y + 1);
    4. uint w = (uint)(1812433253 * z + 1);
     
    Last edited: Feb 7, 2021
    Macklin likes this.
  6. Macklin

    Macklin

    Joined:
    Jun 13, 2015
    Posts:
    11
    Wow, I am in awe! Thank you so much for digging into this, you have no idea how much work you've saved me (and I'm sure others)!

    Initially I spent a few days looking at the way other implementations handle it, but didn't really get any results. So I conceded and figured this would just be an unknown, settling on calculating the state values in advance. I did read somewhere about the use of Mersenne Twister, but didn't think much beyond that.

    I've tested this as far back as Unity 4.6, but as mentioned earlier, this could change at any point in the future - so applications shouldn't rely on it being the same forever.

    https://gist.github.com/macklinb/a00be6b616cbf20fa95e4227575fe50b
     
    Last edited: Feb 25, 2021