What seed value should I use for erand48() in C?

2019-07-18 03:28发布

问题:

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?

回答1:

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.



回答2:

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...



回答3:

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.