22

I am writing some embedded code in C and need to use the rand() function. Unfortunately, rand() is not supported in the library for the controller. I need a simple implementation that is fast, but more importantly has little space overhead, that produces relatively high-quality random numbers. Does anyone know which algorithm to use or sample code?

EDIT: It's for image processing, so "relatively high quality" means decent cycle length and good uniform properties.

Craig McQueen
  • 41,871
  • 30
  • 130
  • 181
rlbond
  • 65,341
  • 56
  • 178
  • 228
  • 1
    What are you looking for, more specifically? Do you need a long cycle length? How big numbers are the numbers we're talking about (16-bit, 32-bit, whatever)? How random do they have to be? By "space overhead", are you referring to ROM limitations, RAM limitations, or both? – David Thornley Jul 22 '09 at 18:51
  • If you have a SysTick or something else which can be used to count time from power up to current time, then you can use that counter as a seed for some of the random generators shown below. – avra Jul 02 '12 at 08:36
  • Here's an implementation of the [Mersenne Twister](https://gist.github.com/superwills/0ffc72aeeaeeab2b150d) in a C++ class – bobobobo Aug 03 '15 at 10:54

11 Answers11

25

Check out this collection of random number generators from George Marsaglia. He's a leading expert in random number generation, so I'd be confident using anything he recommends. The generators in that list are tiny, some requiring only a couple unsigned longs as state.

Marsaglia's generators are definitely "high quality" by your standards of long period and good uniform distribution. They pass stringent statistical tests, though they wouldn't do for cryptography.

John D. Cook
  • 29,517
  • 10
  • 67
  • 94
  • 2
    Thanks for the reference to that. I've done a little research, and found ["KISS: a Bit Too Simple", by Greg Rose](http://eprint.iacr.org/2011/007.pdf) which (a) warns against "bad" seed values in MWC and (b) casts doubt on the SHR3 generator. [This 2003 post by Marsaglia](http://groups.google.com/group/sci.math/msg/9959175f66dd138f) gives a slightly different SHR3 which fixes the problems the earlier one had. I would be happy to use the KISS-style generator, as long as I check and avoid "bad" seeds, and ensure that I am using the better SHR3 generator. – Craig McQueen Jan 20 '11 at 02:06
  • Marsaglia made major contributions in the 90s to PRNGs, however, today I'd probably look to L'Ecuyer or Matsumoto, I think. That being said, if the OP doesn't do major simulation on his embedded devices I guess most of the earlier good generators can be used as well :-) – Joey Jan 26 '11 at 01:13
  • 2
    @Joey: Marsaglia's KISS generators still perform well in [L'Ecuyer's `TestU01` RNG tests](http://www.iro.umontreal.ca/~lecuyer/myftp/papers/testu01.pdf), passing tests which even some of L'Ecuyer's own RNGs such as `LFSR113` fail. – Craig McQueen Feb 20 '11 at 22:50
13

Use the C code for LFSR113 from L'écuyer:

unsigned int lfsr113_Bits (void)
{
   static unsigned int z1 = 12345, z2 = 12345, z3 = 12345, z4 = 12345;
   unsigned int b;
   b  = ((z1 << 6) ^ z1) >> 13;
   z1 = ((z1 & 4294967294U) << 18) ^ b;
   b  = ((z2 << 2) ^ z2) >> 27; 
   z2 = ((z2 & 4294967288U) << 2) ^ b;
   b  = ((z3 << 13) ^ z3) >> 21;
   z3 = ((z3 & 4294967280U) << 7) ^ b;
   b  = ((z4 << 3) ^ z4) >> 12;
   z4 = ((z4 & 4294967168U) << 13) ^ b;
   return (z1 ^ z2 ^ z3 ^ z4);
}

Very high quality and fast. Do NOT use rand() for anything. It is worse than useless.

Craig McQueen
  • 41,871
  • 30
  • 130
  • 181
  • 3
    Note that it assumes a 32-bit int, which may not be appropriate for an embedded platform. – tomlogic Jun 07 '11 at 17:49
  • 1
    Why exactly is this random? – me me May 02 '13 at 01:52
  • 1
    me me, it depends on what you mean by random. LFSR RNG's are based on a well-defined mathematical recurrence with a known period and good statistical properties (perceived randomness). Unless you're taking entropy from some physical phenomenon (e.g. line noise), any RNG you use is going to be similarly pseudo-random. (Cryptographically secure RNG's are more complex but still pseudo-random). The stronger Mersenne Twister is a twisted GFSR, so it's related to the above, and the weaker Xorshift is a special case of LFSR. CMWC RNG's can be both faster and stronger than any of those though. – Mike S Jul 18 '13 at 22:22
  • 1
    @me me: It is pseudo-random, static variables hold seed values. It always returns the same sequence of numbers. – k3a Jul 19 '15 at 13:31
  • Probably a mistake to put the seed as static in the answer, but this looks promising, just make sure you provide the seed externally. I'll try it. As for 32 bit, the actual source uses uint32_t instead of unsigned int, so that shouldn't be an issue either. – AlexKven Aug 18 '18 at 16:33
  • After seeing this and reading a bit, googling around, I found https://github.com/cmcqueen/simplerandom which is apparently this answer's author's collection of useful random functions! In it I see this comment, which is relevant to the seeds used (which initially looked fishy) /**** VERY IMPORTANT **** : The initial seeds z1, z2, z3, z4 MUST be larger than 1, 7, 15, and 127 respectively. */ Useful if you plan on changing the seed values! – simap Feb 03 '22 at 17:09
4

Here is a link to a ANSI C implementation of a few random number generators.

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
3

I've made a collection of random number generators, "simplerandom", that are compact and suitable for embedded systems. The collection is available in C and Python.

I've looked around for a bunch of simple and decent ones I could find, and put them together in a small package. They include several Marsaglia generators (KISS, MWC, SHR3), and a couple of L'Ecuyer LFSR ones.

All the generators return an unsigned 32-bit integer, and typically have a state made of 1 to 4 32-bit unsigned integers.

Interestingly, I found a few issues with the Marsaglia generators, and I've tried to fix/improve all those issues. Those issues were:

  • SHR3 generator (component of Marsaglia's 1999 KISS generator) was broken.
  • MWC low 16 bits have only an approx 229.1 period. So I made a slightly improved MWC, which gives the low 16 bits a 259.3 period, which is the overall period of this generator.

I uncovered a few issues with seeding, and tried to make robust seeding (initialisation) procedures, so they won't break if you give them a "bad" seed value.

Craig McQueen
  • 41,871
  • 30
  • 130
  • 181
  • This is an older post, but I want to make a correction: The low 16-bits of Marsaglia's 2x16 MWC generator actually appear to have a period of 589823999 = 2^29.1357092836584, not 2^16. (This is possible because there are 32-bits of state including carry.) The period is the order of b mod p, where b = 2^16 and p = 18000 * 2^16 - 1. The order = 589823999 = (p - 1)/2 in this case. The best you can do with a base-65536 MWC and a 16-bit multiplier is a period of 2^30.9990971519312 with a multiplier of 65495. The best safe prime is 65184, giving a period of 2^30.9922302642905. – Mike S Jul 18 '13 at 21:14
  • The high bits of Marsaglia's 2x16 MWC generator have a period of 1211400191 = 2^30.1740283979574, and the low bits have a period of 589823999 = 2^29.1357092836584. The concatenation of the two has a period of 714512905044983809 = 2^59.3097376816159, but the individual high and low words still have independent low periods. Your modified MWC2 increases the output quality of the lower bits without breaking the mathematical foundation of the underlying generator, makes the period of the low bits the same as the total combination, and helps the high bits as well...but I'm not sure by how much. – Mike S Jul 18 '13 at 21:27
  • Yes, you're quite right. I said MWC low 16 bits have only a 2^16 period, but I should have said a 2^29.1 period. I've edited it accordingly. But I don't think my change in MWC2 makes any difference to the high bits. – Craig McQueen Jul 18 '13 at 23:48
  • The difference your changes make to the high bits is subtle: When you add the high bits and the low bits, occasionally that will carry another 1 bit into the high bits, thereby slightly altering the high bits about half of the time. Actually though, I was mistaken about the period of the high bits in the first place: I said they have an independent period of 2^30.1740283979574, but they actually have the full period of 2^59.3097376816159, because Marsaglia was already constructing them from adding the low 16 bits of znew and the high 16 bits of wnew. I didn't catch the latter at first. – Mike S Jul 19 '13 at 21:10
  • I was under the impression that your changes would help the period of the high bits as well as the low bits (randomness, not so much), but since the high bits already had the full period, it doesn't really matter. :p Actually, I just made another mistake in my last comment: The carried-over-1-bit will happen much less than half the time...just occasionally. BTW, good catch finding extra bad seeds with MWC. It's a little scary though that they don't seem to fit an obvious pattern that could be generalized to other MWC generators though... – Mike S Jul 19 '13 at 21:14
  • Oops, I just realized the bad seeds you discovered are all larger than the maximum value the base-65536 generators can produce, but the seed for a lag-1 MWC is setting the value of the "last" return by decree. Since a base-65536 generator can only return a value <= 65535, it stands to reason that some larger seed values might lead to "out-of-spec" behavior. Marsaglia himself used larger seeds for this generator as well, but I wonder if they should be limited to (seed % base) in the first place. Aside from those, the bad initial states seem more predictable. – Mike S Jul 20 '13 at 03:41
  • As it turns out, the bad seeds that are > 65536 actually lead to the other bad condition I mentioned: Where the last return value = the max (65535 in this case) and the carry is the multiplier - 1. That's kind of a relief, because at least it demonstrates a pattern. :) – Mike S Jul 20 '13 at 03:55
2

Mersenne twister

A bit from Wikipedia:

  • It was designed to have a period of 219937 − 1 (the creators of the algorithm proved this property). In practice, there is little reason to use a larger period, as most applications do not require 219937 unique combinations (219937 is approximately 4.3 × 106001; this is many orders of magnitude larger than the estimated number of particles in the observable universe, which is 1080).
  • It has a very high order of dimensional equidistribution (see linear congruential generator). This implies that there is negligible serial correlation between successive values in the output sequence.
  • It passes numerous tests for statistical randomness, including the Diehard tests. It passes most, but not all, of the even more stringent TestU01 Crush randomness tests.

  • source code for many languages available on the link.

Yongyi Chen
  • 113
  • 4
Liran Orevi
  • 4,755
  • 7
  • 47
  • 64
2

I recommend the academic paper Two Fast Implementations of the Minimal Standard Random Number Generator by David Carta. You can find free PDF through Google. The original paper on the Minimal Standard Random Number Generator is also worth reading.

Carta's code gives fast, high-quality random numbers on 32-bit machines. For a more thorough evaluation, see the paper.

Craig McQueen
  • 41,871
  • 30
  • 130
  • 181
Norman Ramsey
  • 198,648
  • 61
  • 360
  • 533
1

I'd take one from the GNU C library, the source is available to browse online.

http://qa.coreboot.org/docs/libpayload/rand_8c-source.html

But if you have any concern at all about the quality of the random numbers, you should probably look at more carefully written mathematically libraries. It's a big subject and the standard rand implementations aren't highly thought of by experts.

Here's another possibility: http://www.boost.org/doc/libs/1_39_0/libs/random/index.html

(If you find you have too many options, you could always pick one at random.)

Daniel Earwicker
  • 114,894
  • 38
  • 205
  • 284
  • The `rand.c` source code link seems broken. Try the git repo for [`rand.c`](http://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/rand.c;h=d8458da970c05834ddd187e971c2f024d77e25f2;hb=HEAD) and [`random.c`](http://sourceware.org/git/?p=glibc.git;a=blob;f=stdlib/random.c;h=8a32ee7103fb82fb61a27b182314dbc78098031d;hb=HEAD) – Craig McQueen Feb 14 '11 at 01:05
  • Be aware that code is going to be under the GPL or LGPL. Always know the license of your sources. – davenpcj Apr 17 '13 at 23:56
1

I found this: Simple Random Number Generation, by John D. Cook.

It should be easy to adapt to C, given that it's only a few lines of code.

Edit: and you could clarify what you mean by "relatively high-quality". Are you generating encryption keys for nuclear launch codes, or random numbers for a game of poker?

Craig McQueen
  • 41,871
  • 30
  • 130
  • 181
erjiang
  • 44,417
  • 10
  • 64
  • 100
  • 3
    That's by John D. Cook who wrote [that other answer](http://stackoverflow.com/questions/1167253/implementation-of-rand/1167298#1167298), and it's based on the Marsaglia post he linked to in his answer. – Craig McQueen Jan 25 '11 at 11:38
1

Better yet, use multiple linear feedback shift registers combine them together.

Assuming that sizeof(unsigned) == 4:

unsigned t1 = 0, t2 = 0;

unsigned random()
{
    unsigned b;

    b = t1 ^ (t1 >> 2) ^ (t1 >> 6) ^ (t1 >> 7);
    t1 = (t1 >> 1) | (~b << 31);

    b = (t2 << 1) ^ (t2 << 2) ^ (t1 << 3) ^ (t2 << 4);
    t2 = (t2 << 1) | (~b >> 31);

    return t1 ^ t2;
}
BenW
  • 489
  • 3
  • 8
0

The standard solution is to use a linear feedback shift register.

Craig McQueen
  • 41,871
  • 30
  • 130
  • 181
tetromino
  • 3,490
  • 1
  • 15
  • 8
0

There is one simple RNG named KISS, it is one random number generator according to three numbers.

/* Implementation of a 32-bit KISS generator which uses no multiply instructions */ 

static unsigned int x=123456789,y=234567891,z=345678912,w=456789123,c=0; 

unsigned int JKISS32() { 
    int t; 

    y ^= (y<<5); y ^= (y>>7); y ^= (y<<22); 

    t = z+w+c; z = w; c = t < 0; w = t&2147483647; 

    x += 1411392427; 

    return x + y + w; 
}

Also there is one web site to test RNG http://www.phy.duke.edu/~rgb/General/dieharder.php

zangw
  • 43,869
  • 19
  • 177
  • 214