I need to build an in-place pseudo-random number generator with an adjustable period. In addition, there must be no collisions within one period. That is, the following must return true:
// prng is "generated" at run-time
// (though a by-hand solution would work)
bool test(func prng, int period) {
int seed = 0; // Any number should work
int cur = seed;
for (int i = 0; i <= period; ++i) {
cur = prng(cur);
if (cur == seed) {
if (i == period) {
// We hit our period on target
return true;
}
// Period too low (we hit our seed already!)
return false;
}
}
// Period too high
return false;
}
(Example is pseudocode; an answer in any commonly readable language (C++, Python, Haskell, etc.) is acceptable.)
The PRNG must not depend on mutable static state when generating the numbers. That is, I can't have a large table of already-returned numbers or something like that. It should only rely on the given input for generating the next term.
The algorithm does not have to be cryptographically strong (of course), or "strongly" random. However, x % period
is not acceptable; it has to be at least somewhat random.
I have looked into linear congruential generators, but that seems to be the wrong path to take for my specific constraints.
(Brute-forcing is not an option, unless it's relatively quick (a few seconds).)
I think a good candidate is a Fibonacci Linear Feedback Shift Register (LFSR).
You can get the relevant info and algorithms from Wikipedia.
Just an excerpt:
The only problem is that the periods for the LFSR are always of the form 2N-1.
You could overcome this noting that for any wanted period P, choosing the N that gives you the "next" 2N-1 value leaves potentially 2(N-1)-1 numbers to supress from the cycle (because if you need to supress more than that, just generate with N-1).
So, to supress k values (k = ((2N-1) - P) ⋳ {1 ... ,2(N-1)-1}) you can add a little logic such as