How to generate a random int in C?
How to generate a random int in C?
Question
Is there a function to generate a random int number in C? Or will I have to use a third party library?
Accepted Answer
Note: Don't use
rand()
for security. If you need a cryptographically secure number, see this answer instead.
#include <time.h>
#include <stdlib.h>
srand(time(NULL)); // Initialization, should only be called once.
int r = rand(); // Returns a pseudo-random integer between 0 and RAND_MAX.
Edit: On Linux, you might prefer to use random and srandom.
Read more... Read less...
The rand()
function in <stdlib.h>
returns a pseudo-random integer between 0 and RAND_MAX
. You can use srand(unsigned int seed)
to set a seed.
It's common practice to use the %
operator in conjunction with rand()
to get a different range (though bear in mind that this throws off the uniformity somewhat). For example:
/* random int between 0 and 19 */
int r = rand() % 20;
If you really care about uniformity you can do something like this:
/* Returns an integer in the range [0, n).
*
* Uses rand(), and so is affected-by/affects the same seed.
*/
int randint(int n) {
if ((n - 1) == RAND_MAX) {
return rand();
} else {
// Supporting larger values for n would requires an even more
// elaborate implementation that combines multiple calls to rand()
assert (n <= RAND_MAX)
// Chop off all of the values that would cause skew...
int end = RAND_MAX / n; // truncate skew
assert (end > 0);
end *= n;
// ... and ignore results from rand() that fall above that limit.
// (Worst case the loop condition should succeed 50% of the time,
// so we can expect to bail out of this loop pretty quickly.)
int r;
while ((r = rand()) >= end);
return r % n;
}
}
If you need secure random characters or integers:
As addressed in how to safely generate random numbers in various programming languages, you'll want to do one of the following:
- Use libsodium's
randombytes
API - Re-implement what you need from libsodium's sysrandom implementation yourself, very carefully
- More broadly, use
/dev/urandom
, not/dev/random
. Not OpenSSL (or other userspace PRNGs).
For example:
#include "sodium.h"
int foo()
{
char myString[32];
uint32_t myInt;
if (sodium_init() < 0) {
/* panic! the library couldn't be initialized, it is not safe to use */
return 1;
}
/* myString will be an array of 32 random bytes, not null-terminated */
randombytes_buf(myString, 32);
/* myInt will be a random number between 0 and 9 */
myInt = randombytes_uniform(10);
}
randombytes_uniform()
is cryptographically secure and unbiased.
Lets go through this. First we use the srand() function to seed the randomizer. Basically, the computer can generate random numbers based on the number that is fed to srand(). If you gave the same seed value, then the same random numbers would be generated every time.
Therefore, we have to seed the randomizer with a value that is always changing. We do this by feeding it the value of the current time with the time() function.
Now, when we call rand(), a new random number will be produced every time.
#include <stdio.h>
int random_number(int min_num, int max_num);
int main(void)
{
printf("Min : 1 Max : 40 %d\n", random_number(1,40));
printf("Min : 100 Max : 1000 %d\n",random_number(100,1000));
return 0;
}
int random_number(int min_num, int max_num)
{
int result = 0, low_num = 0, hi_num = 0;
if (min_num < max_num)
{
low_num = min_num;
hi_num = max_num + 1; // include max_num in output
} else {
low_num = max_num + 1; // include max_num in output
hi_num = min_num;
}
srand(time(NULL));
result = (rand() % (hi_num - low_num)) + low_num;
return result;
}
If you need better quality pseudo random numbers than what stdlib
provides, check out Mersenne Twister. It's faster, too. Sample implementations are plentiful, for example here.
The standard C function is rand()
. It's good enough to deal cards for solitaire, but it's awful. Many implementations of rand()
cycle through a short list of numbers, and the low bits have shorter cycles. The way that some programs call rand()
is awful, and calculating a good seed to pass to srand()
is hard.
The best way to generate random numbers in C is to use a third-party library like OpenSSL. For example,
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/rand.h>
/* Random integer in [0, limit) */
unsigned int random_uint(unsigned int limit) {
union {
unsigned int i;
unsigned char c[sizeof(unsigned int)];
} u;
do {
if (!RAND_bytes(u.c, sizeof(u.c))) {
fprintf(stderr, "Can't get random bytes!\n");
exit(1);
}
} while (u.i < (-limit % limit)); /* u.i < (2**size % limit) */
return u.i % limit;
}
/* Random double in [0.0, 1.0) */
double random_double() {
union {
uint64_t i;
unsigned char c[sizeof(uint64_t)];
} u;
if (!RAND_bytes(u.c, sizeof(u.c))) {
fprintf(stderr, "Can't get random bytes!\n");
exit(1);
}
/* 53 bits / 2**53 */
return (u.i >> 11) * (1.0/9007199254740992.0);
}
int main() {
printf("Dice: %d\n", (int)(random_uint(6) + 1));
printf("Double: %f\n", random_double());
return 0;
}
Why so much code? Other languages like Java and Ruby have functions for random integers or floats. OpenSSL only gives random bytes, so I try to mimic how Java or Ruby would transform them into integers or floats.
For integers, we want to avoid modulo bias. Suppose that we got some random 4 digit integers from rand() % 10000
, but rand()
can only return 0 to 32767 (as it does in Microsoft Windows). Each number from 0 to 2767 would appear more often than each number from 2768 to 9999. To remove the bias, we can retry rand()
while the value is below 2768, because the 30000 values from 2768 to 32767 map uniformly onto the 10000 values from 0 to 9999.
For floats, we want 53 random bits, because a double
holds 53 bits of precision (assuming it's an IEEE double). If we use more than 53 bits, we get rounding bias. Some programmers write code like rand() / (double)RAND_MAX
, but rand()
might return only 31 bits, or only 15 bits in Windows.
OpenSSL's RAND_bytes()
seeds itself, perhaps by reading /dev/urandom
in Linux. If we need many random numbers, it would be too slow to read them all from /dev/urandom
, because they must be copied from the kernel. It is faster to allow OpenSSL to generate more random numbers from a seed.
More about random numbers:
- Perl's Perl_seed() is an example of how to calculate a seed in C for
srand()
. It mixes bits from the current time, the process ID, and some pointers, if it can't read/dev/urandom
. - OpenBSD's arc4random_uniform() explains modulo bias.
- Java API for java.util.Random describes algorithms for removing bias from random integers, and packing 53 bits into random floats.