I am new to C programming, and i've read that erand48() is a good option for thread-safe random number generation. However the function takes a seed value of: unsigned short int array[3]
Any recommendations as to what this seed value should be initialized to?
Alright. So first, let me make it clear that the PRNG in libc
is deterministic (that's why it wants a seed), uses an LCG - meaning it's easy to predict all of the values once you have a few, and is therefore insecure.
Now then. erand48()
returns a random floating point value of double
size from a uniform distribution of pseudorandom real numbers. It doesn't take a seed value per se, but rather requires you to provide a state buffer. Here's the full declaration:
double erand48(unsigned short xsubi[3]);
The state buffer, amusingly enough, must be seeded by a random value in order for the generator to work. My first thought was to read from /dev/urandom
.
We can do that with something like this (using unbuffered reads to prevent wastage from such small reads):
#include <stdio.h>
#include <stdlib.h>
void *thread_f (void *i) {
// setup unbuffered urandom
urandom = fopen ("/dev/urandom", "r");
setvbuf (urandom, NULL, _IONBF, 0); // turn off buffering
// setup state buffer
unsigned short randstate[3];
// fgetc() returns a `char`, we need to fill a `short`
randstate[0] = (fgetc (urandom) << 8) | fgetc (urandom);
randstate[1] = (fgetc (urandom) << 8) | fgetc (urandom);
randstate[2] = (fgetc (urandom) << 8) | fgetc (urandom);
// cleanup urandom
fclose (urandom);
// you can now use erand48 (randstate);
... // do whatever work you need to do
return result;
}
This is thread safe, and even ensures a relatively safe seed value for all of the threads.
Of course, if speed isn't too much of an issue (ie: you can live with a small loss of speed) and you can live with integers, then doing unbuffered reads directly from /dev/urandom
is a perfect solution. Even better, /dev/urandom provides secure, unpredictable pseudorandom integers (well, technically, a byte stream, but they will always work as integers as long as you match the size) that are also usually uniformly distributed.
Plus, /dev/urandom
periodically has entropy injected into it and refreshed, ensuring that you have a decent supply of fairly random numbers.
Just my 2 cents...
We used the same function-family in one of our projects sometime ago. The product had to work on Windows/Unix, so we did not use /dev/random.
Instead, we concatenated the host_ip_addr + time() + process_id + thread_id as a string and generated a hash from this string. And then, we spread XOR'ed the trailing bytes onto the 6 leading bytes. We used that final data as our seed...
The rand48 family is a 48 bit LCG, meaning something like
double erand48(uint48_t *state) {
*state = *state * A + C; /* A and C are suitable values */
return *state / 281474976710656.0; /* that's 2^48 */
}
... except that I've never seen a computer with an uint48_t, so what you pass around is an array with 48 bits of state (3x16 assuming that a short has 16 bits).
You can seed it with anything - just get 48 random bits and shove them into array[3], from /dev/urandom, from time/pid/whatever.
A few gotchas:
- lcong48() - forget it even exists
- "how do I make sure that two seeds are not ridiculously close to each other?" - don't, it would take less time to just use another rng.