Pseudo-random number generation library and tools. Part of the OneJoker project.

Project maintained by Lee Daniel Crocker
Lee's blog is etceterology.

onejoker
CC-0: To the extent possible under law, I, Lee Daniel Crocker waive all copyright and related or neighboring rights to all creative works original to me.

Using the library in your C code

Quickstart

All of the functions of the RandLib C API begin with ojr_. There are two ways to interact with the library. You can create ojr_generator objects with a specific algorithm and pass a pointer to your generator to all the functions, or you can just call the functions with the value DEFGEN as the generator argument and get the library’s “default” generator. Here’s a simple example of using the C API to print 10 random numbers in the range 0 to 99, using the Mersenne Twister algorithm.

#include <stdio.h>
#include "ojrandlib.h"

ojr_generator *g = ojr_open("mt19937");

for (int i = 0; i < 10; ++i) {
    printf("%d ", ojr_rand(g, 100));
}
printf("\n");
ojr_close(g);

The single header file ojrandlib.h has all the needed declarations and definitions for the library. You will also have to link your code to the library (with gcc, for example, add -lojrand to the link command). We first create a new generator object with ojr_open(), which returns a pointer to the newly created generator. This allocates memory for the generator’s internal structures, so we have to call ojr_close() to free those up later. The generator is automatically seeded appropriately from whatever randomness is available from your operating system (you could seed it yourself if you wanted a reproducible sequence). Finally, ojr_rand() fetches a random integer in the given range with equal probability.

That’s all you need for basic use: for example, writing a simple card game. There are many other functions available for more complex needs, detailed below.

Basic generator functions

ojr_generator *ojr_open(char *algorithm)

Creates a new generator using the algorithm named by the single argument. If you pass NULL, a reasonable default algorithm will be chosen (currently this is “mwc8222”). See the ojr_algorithm_* functions below for how to get information on the available algorithms. The generator objects returned by this function are totally independent of each other, regardless of algorithm. In particular, if you create two generators with the same algorithm and same seed, they will independently produce the same stream of random numbers (this can be handy for writing test programs, for example).

ojr_close(ojr_generator *g)

Closes and frees all allocated resources of the given generator. Using the pointer after this will produce undefined results. If you fail to close a generator created by ojr_open(), it will be closed when the library is unloaded, and produce a warning message. When the library is unloaded is dependent on the OS, so it is best to avoid this memory leak.

ojr_system_seed(ojr_generator *g)

Seeds the given generator with random values derived from whatever entropy is available from your operating system. The size of the seed will be chosen to suit the algorithm. If you need to save the value of a system-generated seed for reproduction later, you will have to instead call ojr_get_system_entropy() with appropriate arguments followed by ojr_array_seed() (see below).

ojr_int_seed(ojr_generator *g, int val)

Seeds the generator with the integer value val.

ojr_array_seed(ojr_generator *g, uint32_t *seed, int count)

Seeds the generator with the given array of the 32-bit unsigned ints. This allows you to produce many more random sequences than are possible with a single integer seed, while still allowing reproducibility.

ojr_reseed(ojr_generator *g, uint32_t *seed, int count)

Applies a new seed to an existing generator to change its state “on the fly” without re-initializing. This function should not be needed by most applications. It could be used, for example, to occasionally add extra system entropy to a generator to make it more cryptographically secure. This eliminates reproducibility.

ojr_discard(ojr_generator *g, int count)

Advances the generator count steps wihout producing actual output. This is roughly equivalent to calling ojr_next32(g) (see below) count times, though it is a bit faster due to reduced function call overhead. Some generator algorithms benefit from discarding some number of values after initialization before using their output.

Producing output

Now that you’ve created and generator and seeded it, you can use it to produce random numbers in various ways. In all of these functions, you can also pass the value DEFGEN as the generator argument rather than opening your own generator.

uint32_t ojr_next32(ojr_generator *g)

Produces a random 32-bit unsigned integer. All of the algorithms included in ojrandlib are natively 32-bit generators, so the most basic way to get random values is to get them as 32-bit unsigned integers. All of the other producer functions are based on this one.

uint16_t ojr_next16(ojr_generator *g)

Produce a 16-bit random unsigned integer.

uint64_t ojr_next64(ojr_generator *g)

Produce a 64-bit random unsigned integer.

int ojr_rand(ojr_generator *g, int limit)

Produces a random integer in the range 0…(limit-1). This is the most useful function for randomly choosing from among a small set of alternatives, for example, selecting a random card from a deck of cards. The numbers are guaranteed to be balanced. That is, if you ask for numbers from 0 to 2, you will get 0, 1, and 2 with exactly equal probability. The maximum limit for this function is 32768.

double ojr_next_double(ojr_generator *g)

Produces a random double value uniformly distributed in the half-open interval [0.0, 1.0). That is, it may produce the value 0.0, but will never produce 1.0, and will produce every representable value in between with equal probability.

double ojr_next_signed_double(ojr_generator *g)

Produces a random double value uniformly distributed in the open interval (-1.0, 1.0). That is, it will never produce the values -1.0 or 1.0, and will produce every representable value in between with equal probability.

double ojr_next_exponential(ojr_generator *g)

Produces a random double value with a probability density of e-x.

double ojr_next_normal(ojr_generator *g)

Produces a random double value normally distributed about a mean of 0.0 with a standard deviation of 1.0.

Non-generator functions

These functions do not take a generator object argument because they provide information or services independent of any particular generator.

ojr_get_system_entropy(uint32_t *seed, int count)

Fills the given array (which must have room for count 32-bit unsinged integers) with random bits from whatever source of randomness your OS provides. On Linux-like systems, this will be /dev/urandom. On Windows, CryptGenRandom() from the cryptography library will be used. If neither of these is available, the system time and process ID will be used. You should only use this function manually if you need both a good quality seed for a generator and need to save it for reproducibility later. Otherwise, simply using ojr_system_seed() on the generator is much simpler.

ojr_get_random_org(uint32_t *seed, int count)

Fills the given array (which must have room for count 32-bit unsinged integers) with random bits from the website random.org, which provides random numbers based on atmospheric noise. It is recommended that this be used for seeding PRNGs or other applications that don’t need large quantities of random numbers. The service is limited in speed by your Internet connection and limited in quantity to a daily quota, because it is a free service (although you can pay them for a larger quota).

ojr_algorithm_count()

Returns the number of algorithms available in the system.

char *ojr_algorithm_name(int a)

Returns the name of the algorithm at the given index. Algorithms are identified by an integer from 1 to the number available. So, to list all the available algorithms by name, one could use code like the following:

int i, n = ojr_algorithm_count();

for (i = 1; i <= n; ++i) {
    printf("%s\n", ojr_algorithm_name(i));
}

Passing a value of 0 will return the name of the “default” algorithm, which is the one that will be used when you call ojr_open(NULL).