3

How to write two functions for generating random numbers that supporting next and previous?

I mean how to write two functions: next_number() and previous_number(), that next_number() function generates a new random number and previous_number() function generates previously generated random number.

for example:

int next_number()
{
   // ...?
}

int previous_number()
{
   // ...?
}

int num;

// Forward random number generating.
// ---> 54, 86, 32, 46, 17
num = next_number(); // num = 54
num = next_number(); // num = 86
num = next_number(); // num = 32
num = next_number(); // num = 46
num = next_number(); // num = 17

// Backward random number generating.
// <--- 17, 46, 32, 86, 54
num = previous_number(); // num = 46
num = previous_number(); // num = 32
num = previous_number(); // num = 86
num = previous_number(); // num = 54
Amir Saniyan
  • 13,014
  • 20
  • 92
  • 137

5 Answers5

5

You can trivially do this with a Pseudo-Random Function (PRF).

Such functions take a key and a value, and output a pseudo-random number based on them. You'd select a key from /dev/random that remains the same for the run of the program, and then feed the function an integer that you increment to go forward or decrement to go back.

Here's an example in pseudo-code:

initialize():
    Key = sufficiently many bytes from /dev/random
    N = 0

next_number():
    N = N + 1
    return my_prf(Key, N)

previous_number():
    N = N - 1
    return my_prf(Key, N)

Strong, Pseudo-Random Functions are found in most cryptography libraries. As rici points out, you can also use any encryption function (encryption functions are pseudo-random permutations, a subset of PRFs, and the period is so huge that the difference doesn't matter).

that other guy
  • 116,971
  • 11
  • 170
  • 194
  • You call this random? – Erki Aring Aug 06 '14 at 19:53
  • 1
    I call it pseudo-random. That's the P in PRF. – that other guy Aug 06 '14 at 19:53
  • 1
    I'm pretty sure you are effectively killing the R part :) http://stackoverflow.com/a/23083105/3142427 – Erki Aring Aug 06 '14 at 20:02
  • 1
    @ErkiA well yes, but the point of pseudo random number generators is that they have an output that passes certain statistical tests, not that they're actually random (which, by design, they are not) – harold Aug 06 '14 at 20:04
  • 2
    @harold pseudo-random algorithms that are seeded before each call may fail the tests that they pass when not constantly reseeded. – Mark Ransom Aug 06 '14 at 20:06
  • @MarkRansom This is true. However, no secure pseudo-random algorithm will do this, as it would then be easy to distinguish it from a truly random function. – that other guy Aug 06 '14 at 20:16
  • @that other guy - You are doing it in your example. If you seed your PRNG with sequential numbers every time before calling rand() then you generate numbers that are no more random than the initial sequential numbers. You could use 1,2,3,4 instead without the overhead of PRNG. – Erki Aring Aug 06 '14 at 20:39
  • 2
    @thatotherguy perhaps for a "secure" pseudo-random algorithm, but I know for a fact that Microsoft's `rand()` for example has a problem with a high correlation between the seed and the next random number generated. Reseeding it with an incrementing number wouldn't be random at all. – Mark Ransom Aug 06 '14 at 20:40
  • @MarkRansom fair point. I tried finding a secure PRNG that doesn't drown the example in boilerplate, but they were all either insecure or entropy-gathering so I settled for a textual warning. – that other guy Aug 06 '14 at 21:14
  • 2
    @ErkiA That's also the case if you only seed it once. A PRNG is always deterministic for its input. – that other guy Aug 06 '14 at 21:18
  • @thatotherguy: You're not using a PRNG at all. You're using the prng's seed-mangling function as a hash, on the assumption that the seed-mangling function is not just the identity function. You'd be better off using a real hash function, preferably a cryptographic hash. Or, as I suggest in my answer, an actual cryptographic function. – rici Aug 07 '14 at 03:05
  • @rici I didn't realize how little attention the non-code part of the post would get. I've updated it so that you don't have to read it all. Your answer is a special case of this, using a PRP instead of a general PRF. – that other guy Aug 07 '14 at 03:55
  • @thatotherguy: I think you underestimate the confusion you caused by implying that `(srand(i),rand())` is even vaguely like a PRF (where is the key, for example?) or even that it has anything to do with randomness aside from the use of four letters in its name. (`srand(key);while(i--)rand();return rand();` *would* be a PRF, but not a good one.) Your new answer is much better, but I'm not sure that a naive user could find a PRF "in most cryptography libraries", at least without some guidance. The advantage of my PRP is that it only requires 3DES, AES, Blowfish or other easy-to-find functions. – rici Aug 07 '14 at 04:32
3

Some linear congruential generators (a common but not very good PRNG) are reversible.

They work by next = (a * previous + c) mod m. That's reversible if a has a modular multiplicative inverse mod m. That's often the case, because m is often a power of two and a is usually odd.

For example for the "MSVC" parameters from the table from the first link:

  • m = 232
  • a = 214013
  • c = 2531011

The reverse is:

previous = (current - 2531011) * 0xb9b33155;

With types chosen to make it work modulo 232.

harold
  • 61,398
  • 6
  • 86
  • 164
2

Suppose you have a linear congruential sequence S defined by

S[0] = seed
S[i] = (p * S[i-1] + k) % m

for some p, m, k such that gcd(p, m) == 1. Then you can find q such that (p * q) % m == 1 and compute:

S[i-1] = (q * (S[i] - k)) % m

In other words: if you pick suitable p and precompute q, you can traverse your sequence in either order in O(1) time.

candu
  • 2,827
  • 23
  • 18
2

A reasonably simple way of generating an indexable pseudo-random sequence -- that is, a sequence which looks random, but can be traversed in either direction -- is to choose some (reasonably good) encryption algorithm and a fixed encryption key, and then define:

sequence(i): encrypt(i, known_key)

You don't need to know the value of i, because you can decrypt it from the number:

next(r): encrypt(decrypt(r, known_key) + 1)

prev(r): encrypt(decrypt(r, known_key) - 1)

Consequently, i does not have to be a small integer; since the only arithmetic you need to do to it is addition and subtraction by a small integer, a bignum implementation is trivial. So if you wanted 128-bit pseudorando numbers, you could set the first i to be a 128-bit random number extracted from /dev/random.

You have to keep the entire value of i in static storage, and the period of the pseudorandom numbers cannot be greater than the range of i. That will be true of any solution to this problem, though: since the next() and prev() operators are required to be functions, every value has a unique successor and predecessor, and thus can only appear once in the cycle of values. That's quite different from the Mersenne twister, for example, whose cycle is much larger than 232.

rici
  • 234,347
  • 28
  • 237
  • 341
0

I think what you are asking for is random number generator that is deterministic. This does not make sense because if it is deterministic, it's not random. The only solution is to generate a list of random numbers and then step back and forward in this list.

PS! I know that essentialy all software PRNG-s are deterministic. You can of course use this to create the functionality you need, but don't fool yourself, it has nothing to do with randomness. If your software design requires having deterministic PRNG then you could probably skip the PRNG part at all.

Erki Aring
  • 2,032
  • 13
  • 15
  • All PRNG are deterministic. OP is asking for a pseudo-random sequence whose inverse is also deterministic, which limits its cyclic period to the number of possible random values. – rici Aug 07 '14 at 01:46