Generating Random Values within a Specified Range in C/C++

When programming in C/C++, there are times when you need random numbers within a specified range. This article introduces how to obtain integer random numbers within a specified range, both using C++’s Standard Template Library (STL) and without using it.

STL Method

Since C++11, the STL has included the random library, which allows for various types of random number generation.

It’s worth noting that the code presented in this article is just one implementation example to explain how to use the library, and it is not recommended to copy and paste it for use in all situations. Understand the usage and implement according to your own situation.

How to Generate Random Numbers

You can generate high-quality pseudo-random numbers using the Mersenne Twister by utilizing either the std::mt19937 or std::mt19937_64 class, depending on whether your environment supports 64-bit.

#include <random>

// For a 64-bit environment
uint64_t get_rand() {
    // Random number generator (seed can be specified as an argument)
    static std::mt19937_64 mt64(0);

    // Generates a uniform distribution integer from [0, (2^64)-1]
    return mt64();
}

// For a 32-bit environment
uint32_t get_rand() {
    // Random number generator (seed can be specified as an argument)
    static std::mt19937 mt32(0);

    // Generates a uniform distribution integer from [0, (2^32)-1]
    return mt32();
}

The object is declared static merely to emulate the ease of use similar to the standard C rand function, but it is not mandatory.

Generating Random Numbers within a Specified Range or Distribution

By passing the above-mentioned random number generator to a distribution generator, you can specify the range of random numbers as follows:

#include <random>

uint64_t get_rand_range(uint64_t min_val, uint64_t max_val) {
    // Random number generator
    static std::mt19937_64 mt64(0);

    // Uniform distribution integer (int) distribution generator for [min_val, max_val]
    std::uniform_int_distribution<uint64_t> get_rand_uni_int(min_val, max_val);

    // Generate the random number
    return get_rand_uni_int(mt64);
}

Moreover, by replacing the distribution generator with the following, you can change the type of values or the kind of distribution.

Random Character

If you want a random character from ‘a’ to ‘z’, use the following distribution generator:

// Uniform distribution generator for [a-z]
std::uniform_int_distribution<char> get_rand_uni_char('a', 'z');

Real Numbers from 0 to 1

If you need real numbers from 0 to 1, not integers, do as follows:

// Uniform real distribution generator for [0.0, 1.0)
std::uniform_real_distribution<double> get_rand_uni_real(0.0, 1.0);

Normal Distribution

There are other distributions, such as the normal distribution, available as well:

// Normal distribution generator with mean 0.0 and standard deviation 1.0
std::normal_distribution<double> get_rand_norm_dist(0.0, 1.0);

For more detailed information, please refer to the following:

Non-STL Method

If you do not use convenient libraries like the STL in C++, you can generate random numbers within a specified range using algorithms like the ones below. However, be aware that some of these methods may introduce biases in the values, so consider your use case carefully before deciding.

Let’s assume there exists a function rand() that returns a uniform random integer from 0 to RAND_MAX. Please note that you cannot simply copy and paste the following code as is.

Method 1: Modulo by Maximum Value

A commonly used method involves taking the modulo by the maximum value of the range. You can obtain a random number in the range from min_val to max_val as follows. However, the generated random numbers are not strictly uniform.

int get_rand(int min_val, int max_val) {
    return (int)((rand() % (max_val+1 - min_val)) + min_val);
}

Method 2: Divide by Maximum Random Number, then Multiply by Maximum Value

Another method involves dividing by the maximum random number and then multiplying by the maximum value of the range. If you want a random number in the range from min_val to max_val, do as follows. This method also does not guarantee strict uniformity.

int get_rand(int min_val, int max_val) {
    return (int)(rand() / ((double)RAND_MAX+1.0) * (max_val+1 - min_val) + min_val);
}

Method 3: Ensuring Strict Uniformity

The method described above does not strictly guarantee uniformity, but in practice, it is often sufficient. However, if you need strictly uniform random numbers for your purpose, please use the following algorithm.

int get_rand(int min_val, int max_val) {
    int randmax_limit = (int)(RAND_MAX / (max_val+1 - min_val)) * (max_val+1 - min_val);
    int r;
    while ((r = rand()) > randmax_limit);
    return (r % (max_val+1 - min_val)) + min_val;
}

The biases in Methods 1 and 2 are caused by the range of values obtained from rand() not being a multiple of the range from min_val to max_val. In other words, by limiting the range of rand() to randmax_limit, which is an integer multiple of the range from min_val to max_val, we force the distribution to appear uniform.

Of course, there is no guarantee that this while loop will terminate, but unless a very poor-quality random number generator is used, this is unlikely to be a problem. The probability of satisfying the condition in the while loop depends on min_val and max_val, but it is always less than 50%. Even if it were 50%, the probability of the condition being met ten times in a row is less than 0.1%, so the loop is unlikely to cause issues in most cases.

Conclusion

We introduced methods for generating random numbers within specified ranges in C++. It’s clear to see how convenient and powerful C++’s STL is compared to C.

Last updated on Apr 30, 2024

ads.

Built with Hugo
Theme Stack designed by Jimmy