I\'m trying to make a game with dice, and I need to have random numbers in it (to simulate the sides of the die. I know how to make it between 1 and 6). Using
Whenever you do a basic web search for random number generation
in the C++ programming language this question is usually the first to pop up! I want to throw my hat into the ring to hopefully better clarify the concept of pseudo-random number generation in C++ for future coders that will inevitably search this same question on the web!
Pseudo-random number generation involves the process of utilizing a deterministic algorithm that produces a sequence of numbers whose properties approximately resemble random numbers. I say approximately resemble, because true randomness is a rather elusive mystery in mathematics and computer science. Hence, why the term pseudo-random is utilized to be more pedantically correct!
Before you can actually use a PRNG, i.e., pseudo-random number generator
, you must provide the algorithm with an initial value often referred too as the seed. However, the seed must only be set once before using the algorithm itself!
/// Proper way!
seed( 1234 ) /// Seed set only once...
for( x in range( 0, 10) ):
PRNG( seed ) /// Will work as expected
/// Wrong way!
for( x in rang( 0, 10 ) ):
seed( 1234 ) /// Seed reset for ten iterations!
PRNG( seed ) /// Output will be the same...
Thus, if you want a good sequence of numbers, then you must provide an ample seed to the PRNG!
The backwards compatible standard library of C that C++ has, uses what is called a linear congruential generator found in the cstdlib
header file! This PRNG functions through a discontinuous piecewise function that utilizes modular arithmetic, i.e., a quick algorithm that likes to use the modulo operator '%'
. The following is common usage of this PRNG, with regards to the original question asked by @Predictability:
#include
#include
#include
int main( void )
{
int low_dist = 1;
int high_dist = 6;
std::srand( ( unsigned int )std::time( nullptr ) );
for( int repetition = 0; repetition < 10; ++repetition )
std::cout << low_dist + std::rand() % ( high_dist - low_dist ) << std::endl;
return 0;
}
The common usage of C's PRNG houses a whole host of issues such as:
std::rand()
isn't very intuitive for the proper generation of pseudo-random numbers between a given range, e.g., producing numbers between [1, 6] the way @Predictability wanted.std::rand()
eliminates the possibility of a uniform distribution of pseudo-random numbers, because of the Pigeonhole Principle.std::rand()
gets seeded through std::srand( ( unsigned int )std::time( nullptr ) )
technically isn't correct, because time_t
is considered to be a restricted type. Therefore, the conversion from time_t
to unsigned int
is not guaranteed!For more detailed information about the overall issues of using C's PRNG, and how to possibly circumvent them, please refer to Using rand() (C/C++): Advice for the C standard library’s rand() function!
Since the ISO/IEC 14882:2011 standard was published, i.e., C++11, the random
library has been apart of the C++ programming language for a while now. This library comes equipped with multiple PRNGs, and different distribution types such as: uniform distribution, normal distribution, binomial distribution, etc. The following source code example demonstrates a very basic usage of the random
library, with regards to @Predictability's original question:
#include
#include
#include
using u32 = uint_least32_t;
using engine = std::mt19937;
int main( void )
{
std::random_device os_seed;
const u32 seed = os_seed();
engine generator( seed );
std::uniform_int_distribution< u32 > distribute( 1, 6 );
for( int repetition = 0; repetition < 10; ++repetition )
std::cout << distribute( generator ) << std::endl;
return 0;
}
The 32-bit Mersenne Twister engine, with a uniform distribution of integer values was utilized in the above example. (The name of the engine in source code sounds weird, because its name comes from its period of 2^19937-1 ). The example also uses std::random_device
to seed the engine, which obtains its value from the operating system (If you are using a Linux system, then std::random_device
returns a value from /dev/urandom
).
Take note, that you do not have to use std::random_device
to seed any engine. You can use constants or even the chrono
library! You also don't have to use the 32-bit version of the std::mt19937
engine, there are other options! For more information about the capabilities of the random
library, please refer to cplusplus.com
All in all, C++ programmers should not use std::rand()
anymore, not because its bad, but because the current standard provides better alternatives that are more straight forward and reliable. Hopefully, many of you find this helpful, especially those of you who recently web searched generating random numbers in c++
!