5

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?

  • Why not the same as `srand`: `time`? Since you have a couple more bits to fill, perhaps throw in the `pid` as well. – Jongware Oct 13 '14 at 21:14
  • 1
    @Jongware because `time` ain't updated anywhere close to enough for there to be a difference across threads (1s for `libc:time`, <1ms for `pthread:create`). – haneefmubarak Oct 14 '14 at 05:51
  • @haneef: thanks! I entirely forgot about the threading, and indeed it's likely (if not downright *usually*) multiple threads will be created in the exact same millisecond. – Jongware Oct 14 '14 at 19:18

3 Answers3

3

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.

haneefmubarak
  • 1,911
  • 1
  • 21
  • 32
  • The IO functions ARE thread safe (that's why you have getc_unlocked & friends); (nitpick-on) fgetc returns a single byte, you need two, and the LCG link points to LFSR (nitpick-off) – loreb Oct 15 '14 at 20:23
  • @loreb I don't know why I thought they weren't. Anyways, fixed the LCG and the `fgetc()`. Thanks for pointing it out! – haneefmubarak Oct 16 '14 at 05:25
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...

Malkocoglu
  • 2,522
  • 2
  • 26
  • 32
0

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.

loreb
  • 1,327
  • 1
  • 7
  • 6