views:

143

answers:

4

I need an algorithm that pretty much will turn a unix timestamp into a suitably random number, so that if I "play back" the timestamps I get the same random numbers.

And here's what I mean by suitably:

  1. Most humans will not detect a loop or pattern in the random numbers.
  2. It need not be cryptographically secure.
  3. All numbers must be capable of being generated. (I've found that LFSR don't do this)
  4. The numbers are 32 bit integers

And I would like it to be fairly fast.

So far my idea is to just seed a PRNG over and over, but I'm not sure if that's the best way to handle this.

Any thoughts and ideas will be much appreciated.

Thanks.

+2  A: 

If it doesn't need to be statistically random, perhaps feed the timestamps to MD5 and truncate the hash. The main issue is that I don't know if this would be surjective. Other hashing algorithms might work better.

outis
If the random numbers are for human consumption, it's unlikely that a hash collision would be quickly distinguished. MD4, MD5 would be pretty fast.
memnoch_proxy
I'm going to test the hashing and see how the distribution looks. I'm wanting something that has a distribution that is close to being random, but it doesn't have to be that close, if you catch my drift.
Sam Washburn
I went ahead and marked this as the accepted answer because it was what I used, though the other solutions here may prove to be equally as useful.
Sam Washburn
A: 

I suggest looking at the POSIX-compliant drand48() family of functions. They give decent (but certainly not cryptographic) random numbers, and srand48() takes a 32-bit seed value. They are deterministic, so reusing a given seed will regenerate the same sequence of numbers again.

Jonathan Leffler
I didn't see your answer earlier; mine is pretty much the same, but using `(erand48/nrand48/jrand48)` is more direct than `srand48+(drand48/lrand48/mrand48)`.
ephemient
@ephemient: It's all in the family :-)
Jonathan Leffler
A: 

(timestamp ^ 0x12345678) + 12345678 Is this subtle enough?

If you don't care about reversibility of it you could crc32 each timestamp.

Longpoke
A: 

I would suggest that the easiest thing to do is feed your time to jrand48. Something like

#include <stdlib.h>
int mix(int t) {
    unsigned short x[3] = {t, t<<16, t};
    return jrand48(x);
}

It's reversible (216·x+n≡0x5deece66d·(232+1)·t+0xb mod 248 ⇒ t≡0xdfe05bcb1365·(216·x+n-0xb) mod 248 where n∈[0,216)) but since it's the high 32 bits out of 48-bit, it's actually not too easy. (You can apply jrand48 to x more than once too; as long as you don't apply it 248-1 times, the same sorts of properties will hold.)

ephemient
I'm not understanding reversibility to know whether I need it or not. Is that essentially taking the generated number and getting the seed (timestamp) back?
Sam Washburn
Yes. Treating `x` as a 48-bit number, we set `x=(2^32+1)*t`, then `jrand48` changes it to `0x5deece66d*x+0xb` and returns the high 32 bits. It just takes a little bit of modular arithmetic to work out the inverse, i.e. go from the output of `jrand48` back to the original `t`. This isn't generally in general (32-bit output isn't enough to nail down 48-bit state) but since we only have 32-bit input, it is here.
ephemient
Do you mean 't>>16' rather than 't<<16'? What you have shifts the low-order 16 bits of t to the high order 16 bits, and the conversion to short then discards the high order 16 bits, so you have written '{ t, 0, t }' a funny way.
Jonathan Leffler
D'oh, yes, of course I meant `t>>16` :)
ephemient