Predicting when an RNG will output a given value
A few days ago I wrote about how to pick the seed of a simple random number generator so that a desired output came n values later. The number n was fixed and we varied the seed. In this post, the seed will be fixed and we'll solve for n. In other words, we ask when a pseudorandom sequence will produce a given value.
In principle you could just run the RNG until you get the output you're looking for, but we'll assume such a brute force approach is not feasible or at least not fast enough.
If a LCG (linear congruential generator) has seed z, multiplier a, and modulus m, then the nth output is anz reduced mod m. So our task is to solve
x = anz mod m
for n. If we forget for a moment that we're working with integers mod m, we see that the solution is
n = loga (x / z)
We can actually make this work if we interpret division by z to mean multiplication by the inverse of z mod m and if we interpret the logarithm to be a discrete logarithm. For more on discrete logarithms and one algorithm for computing them, see this post.
In an earlier post I used a = 742938285 and m = 231 - 1 = 2147483647. We set n = 100 and solved for z to make the 100th output equal to 20170816, the date of that post. It turned out that z = 1898888478.
Now let's set the seed z = 1898888478 and ask when the LCG sequence will take on the value x = 20170816. Of course we know that n will turn out to be 100, but let's pretend we don't know that. We'll write a little Python script to find n.
I expect there's a simple way to compute modular inverses using SymPy, but I haven't found it, so I used some code from StackOverflow.
The following code produces n = 100, as expected.
from sympy.ntheory import discrete_logdef egcd(a, b): if a == 0: return (b, 0, 1) else: g, y, x = egcd(b % a, a) return (g, x - (b // a) * y, y)def modinv(a, m): g, x, y = egcd(a, m) if g != 1: raise Exception('modular inverse does not exist') else: return x % ma = 742938285z = 1898888478m = 2**31 - 1x = 20170816zinv = modinv(z, m)n = discrete_log(m, x*zinv, a)print(n)