Hello everyone. I am playing with random numbers, trying to understand how srand() and rand() work together in order to generate some randomized numbers. I understand that srand(time(NULL)) generates a seed according to a large amount of seconds since the first january 1970. However, if I display the seed and a randomized number between 0 and 99 in my console, I can see that this is not really a randomized number because of a clear repetitive sequence - a evidente incrementation. How works the rand() function according to its seed ?
PS : I know that I have not to call srand() because one is enough at the start, but I did that just for the exercise in order to check evolution. Thank you for your help ++
#include <iostream>
#include <Windows.h>
usingnamespace std;
int main()
{
int s = 0;
int ratio = 100;
bool running = true;
while(running)
{
if (GetKeyState(VK_SPACE) & 0x8000)
running = false;
if (time(NULL) != s)
{ // determines how many seconds since 1/1/1970
s = time(NULL);
cout << "Seconds since 1/1/1970 : " << s << endl;
// random number between 0 and ratio
srand(s);
int x = rand() % ratio;
cout << "Randomized number between 0 and " << ratio << " : " << x << endl;
}
}
return 0;
}
Hum... it's more complicated than I thought. On my computer, I have some sequence like 22, 25, 28, 32, 35, 39, etc - like an incrementation by 3 or 4. However, using a compiler on-line, it works as expected generating some randomized numbers (but Windows.h is not embedded - you have to erase this part of code for keyboard events). What it means? My code has a totally different behavior on my PC using VisualStudio 2022. Strange :/
#include <iostream>
usingnamespace std;
int main()
{
int s = 0;
int ratio = 100;
bool running = true;
while(running)
{
if (time(NULL) != s)
{ // determines how many seconds since 1/1/1970
s = time(NULL);
cout << "Seconds since 1/1/1970 : " << s << endl;
// random number between 0 and ratio
srand(s);
int x = rand() % ratio;
cout << "Randomized number between 0 and " << ratio << " : " << x << endl;
}
}
return 0;
}
Thank you for your help. If it's a known issue, it is especially specific to Visual Studio. On my PC, it does not work, but it runs fine on other compilers. I will keep in mind that your method with random works better. Thank you for your help and have a nice day ++
srand() sets the RNG to a particular starting point in a sequence. Calling srand() repeatedly within the same second can cause the RNG to return the same series of pseudo-random numbers.
> How works the rand() function according to its seed ?
rand() may be implemented as a https://en.wikipedia.org/wiki/Linear_congruential_generator in the standard library.
It's basically a toy for student dice rolling homework, nothing more.
It's certainly no basis for serious statistical or cryptographic usage.
For an LCG, you either get back the old value or the new value.
> srand(s);
> int x = rand() % ratio;
It would seem from some of your results that the first value you get back from rand() is going to be your seed.
In other words, it will seem to count up in step with time.
Calling srand more than one time, especially in a loop, is going to reset the sequence back to the start so you get the same "random numbers" again and again.
The C library's random function, rand(), is bad. Really bad at generating even pseudo random numbers.
The C library's srand/rand are broken that even the C standard recommends not using them if there are alternative methods available. C++ introduced those methods with <random>.
I created a standalone header/module interface file from the toolkit proposal in that working paper PDF so whenever I need random numbers in my C++ code I simply include the file and generate my sequences. I wrote the functions in a custom namespace and named the functions to replicate the C function names, srand/rand/, with some extra functionality I use quite a lot when generating random numbers along with some error handling capabilities.
Thank you everyone for your clever and relevant explanations. Without any doubt, they are many advantages to use random() instead rand(). Georges, I read your paper right now. I wish you the best. Have a nice day ++
/* A simple toolkit to help beginners using the <random> library an easier task
*
* pre-C++20 header file */
// shamelessly stolen and adapted from a C++ working paper: WG21 N3551
// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3551.pdf
#ifndef RANDOM_TOOLKIT
#define RANDOM_TOOLKIT
#include <chrono>
#include <random>
#include <stdexcept>
namespace rtk
{
staticbool seeded { false };
inline std::mt19937& urng()
{
static std::mt19937 URNG { };
return URNG;
}
// function to initialize (seed) the PRNG engine
// uses a seed sequence for better randomization
inlinevoid srand(bool FORCE_SEED = false)
{
staticconst std::seed_seq::result_type seeds[] { std::random_device {} (),
std::seed_seq::result_type(std::chrono::system_clock::now().time_since_epoch().count()) };
static std::seed_seq sseq(std::begin(seeds), std::end(seeds));
// the URNG can't be reseeded unless forced
if (!seeded || FORCE_SEED)
{
urng().seed(sseq);
seeded = true;
}
}
// function to initialize the PRNG engine
// using a specified seed
inlinevoid srand(signed seed, bool FORCE_SEED = false)
{
// the URNG can't be reseeded unless forced
if (!seeded || FORCE_SEED)
{
urng().seed(static_cast<unsigned>(seed));
seeded = true;
}
}
// two function overloads to obtain uniform distribution ints and doubles
inlineint rand(int from, int to)
{
static std::uniform_int_distribution<> dist { };
if (from > to) { throw std::invalid_argument("bad int distribution params"); }
return dist(urng(), decltype(dist)::param_type { from, to });
}
inlinedouble rand(double from, double to)
{
static std::uniform_real_distribution<> dist { };
if (from > to) { throw std::invalid_argument("bad double distribution params"); }
return dist(urng(), decltype(dist)::param_type { from, to });
}
// function for rolling dice, and checking if the # of pips is nonstandard
inlineint roll_die(int pips)
{
// check to see if the number of die pips is less than 2
if (pips < 2)
{
return -1;
}
return rand(1, pips);
}
}
#endif
Yes, this looks complicated, a lot of that is from the added functionality I added, but once you have the thing working you don't need to do more than include sight unseen as if it were a standard library header it in your source files to use the toolkit.
Here's a "simple" source file you can use to test the toolkit if you want: