2

I'm interested in generating fast random booleans (or equivalently a Bernoulli(0.5) random variable) in C. Of course if one has a fast random generator with a decent statistical behaviour the problem "sample a random Bernoulli(0.5)" is easily solved: sample x uniformly in (0,1) and return 1 if x<0.5, 0 otherwise.

Suppose speed is the most important thing, now I have two questions/considerations:

  1. Many random doubles generators first generate an integer m uniformly in a certain range [0,M] and then simply return the division m/M. Wouldn't it be faster just to check whether m < M/2 (here M/2 is fixed, so we are saving one division)

    1. Is there any faster way to do it? At the end, we're asking for way less statistical properties here: we're maybe still interested in a long period but, for example, we don't care about the uniformity of the distribution (as long as roughly 50% of the values are in the first half of the range).
Gabriele
  • 469
  • 4
  • 10

2 Answers2

3

Extracting say the last bit of a random number can wreak havoc as linear congruential generators can alternate between odd and even numbers1. A scheme like clock() & 1 would also have ghastly correlation plains.


Consider a solution based on the quick and dirty generator of Donald Kunth: for uint32_t I, sequence

I = 1664525 * I + 1013904223;

and 2 * I < I is the conditional yielding the Boolean drawing. Here I'm relying on the wrap-around behaviour of I which should occur half the time, and a potentially expensive division is avoided.

Testing I <= 0x7FFFFFFF is less flashy and might be faster still, but the hardcoding of the midpoint is not entirely satisfactory.


1 The generator I present here does.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
2

I'm interested in generating fast random booleans

Using a LCG can be fast, yet since OP's needs only a bool result, consider extracting only 1 bit at a time from a reasonable generator and save the rest for later. @Akshay L Aradhya

Example based on @R.. and @R.. code.

extern uint32_t lcg64_temper(uint64_t *seed); // see R.. code

static uint64_t gseed; //  Initialize this in some fashion.
static unsigned gcount = 0;

bool rand_bool(void) {
  static uint32_t rbits;
  if (gcount == 0) {
    gcount = 32;  // I'd consider using 31 here, just to cope with some LCG weaknesses.
    rbits = lcg64_temper(&gseed);  
  }
  gcount--;
  bool b = rbits & 1;
  rbits >>= 1;
  return b;
}
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • I wonder if this will work out faster than my solution in common use cases? – Bathsheba May 09 '18 at 16:48
  • @Bathsheba Many unknowns, particularly the cost of a wide integer multiplication. The higher that cost, the more profitable to extract 1 bit and save the rest. The _fuzzy_ part is how [low a quality random number](http://dilbert.com/strip/2001-10-25) generator can we go to meet OP's need of randomness vs. speed? – chux - Reinstate Monica May 09 '18 at 16:53
  • Upped all the same. – Bathsheba May 09 '18 at 16:54