srand always outputs the same number

Time-appropriate greetings!

I am curently a bit stuck with trying to get a function to generate random integers in a certain range.
I looked at some tutorials and this is what I "came up with":

1
2
3
4
5
6
7
8
9
10
11
12

int GenerateRandomNumberInRange(int Min,int Max)
	{
		int tempvar;
		int MaxAdjusted;
		MaxAdjusted = Max - Min;
		srand(1);
		tempvar = rand() %MaxAdjusted;
		tempvar = tempvar + Min;
		return tempvar;
	}


But when I run the function a few times, it always gives me the same number.
I know that this is a "pseudo RNG", so it will always give the same SEQUENCE of numbers, but it shouldn't just always give out 5 for example.
If the output was something like 1,2,3,4,1,2,3,4 that would make sense (same sequence), but like I said, it just gives me one number.
When I use time() as a seed and run the function in an infinite loop, the output values change every second (makes sense, because the seed changes every second).

What could I do to make this work?

I already searched a bit online, but the only things I found were related to "RNG always giving the same sequence of numbers".
So, I think I am doing something wrong here ...

Any help would be appreciated ...
srand(1) resets the (global) state of rand(). It causes rand() to restart from the beginning of the sequence which srand(1) produces.

Let's say the first number of the sequence that srand(1) produces is 52 [implementation-defined].

If you call:
1
2
3
4
5
6
srand(1);
cout << rand() << ' ';
srand(1);
cout << rand() << ' ';
srand(1);
cout << rand() << ' ';

It will print 52 52 52.

If you call:
1
2
3
4
srand(1);
cout << rand() << ' ';
cout << rand() << ' ';
cout << rand() << ' ';

It will produce 52 2462 77 (for example; just made-up numbers).

The solution is just to not call srand() within a loop. Call it at the beginning of your program, before any loop happens. Otherwise you'll keep resetting the state of the sequence.
Last edited on
Ahhh, ok!
So the srand(seed) is like the "initialization" of the RNG that should only be called once.
Got it!

I will try that, give me a few seconds ...

EDIT:
Ok, I tried it and it works!
Now I am getting a repeated sequence!

At first I tried to use the "random_device rd; and mt19937 - thing", but I had that issue with "always the same output number" and so I thought I did something wrong and tried it with this "simple" pseudo RNG.
But after understanding how to do this, I will use random_device to get actual random numbers.

Thanks!
Last edited on
Ok, I was wrong:
This doesn't seem to work with the random_devic rd; and mt19937 stuff ...
Here is what I tried:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
void InitTheRNG ()
  {
    random_device rd;
    mt19937 gen(rd());
  }

int GenerateRandomNumberInRange(int Min, int Max)
  {
    int tempvar;
    uniform_int_distribution<> distr(Min, Max);
    tempvar = distr(gen);
    return tempvar;
  }

main ()
  {
    InitTheRNG();
    //Do some stuff
    for(int i = 0; i < 50; i++)
      {
          cout << GenerateRandomNumberInRange(50,300);
          cout << "\n";
      }
    //Do some other stuff
  }


And I get the error " 'gen' was not declared in this scope".
So, I thought I'd just put that line mt19937 gen(rd()); into the main RNG function as well.
But then I got the error " 'rd' was not declared in this scope
So, it appears that all of this has to be in the same "scope".
But then, how do I make the "initialization part" only run once?
Maybe with a global variable that gets set to 1 if the "initialization" has already been done?
pass it as a parameter or make it global
may also encapsulate on a class
Pass what as a parameter or make it global?
The "enable the initialization" - thing?
Yes, I think I will do it that way (make it a global variable ...

EDIT:
Nope, still not working:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool RngInitialized = false;

int GenerateRandomNumberInRange(int Min,int Max)
	{
		int tempvar;
		if(RngInitialized == false)
			{
				random_device rd; // obtain a random number from hardware
				mt19937 gen(rd()); // seed the generator
				RngInitialized = true;
			}
		uniform_int_distribution<> distr(Min, Max);
		tempvar = distr(gen);
		
		return tempvar;
	}


I still get the error " 'gen' was not declared in this scope.
Last edited on
You have rd and gen as a local variables within the scope of your if-statement. They are destroyed once the if-statement body ends.
C++11 <random> functionality is a different beast than C-style rand/srand stuff -- there is no implicit global state in the C++11 method.

1
2
3
4
5
void InitTheRNG ()
  {
    random_device rd;
    mt19937 gen(rd());
  }

These objects, random_device rd and mt19937 gen, are local to your function. There is no global state here. That state is destroyed once the InitTheRNG function ends.

- As ne555 said, considering encapsulating the random_device and the mt19937 objects into a class, initialize random_device/mt19937 in the constructor, and then make GenerateRandomNumberInRange be a member function that can be assumed to be initialized.
- One alternative is to use static local variables within your function (one for random_device rd and another for mt19937 gen).
- Another alternative is to make mt19937 gen be a global variable.
- Another alternative is to have InitTheRNG return the mt19937 object, and pass the mt19937 object by reference to your GenerateRandomNumber function.
Last edited on
I thought to have a variable you needed to initialize / define it first:
Like int i = 0; or int i;
But there is nothing like that there, so I am a bit confused ...
At first I tried to use the "random_device rd; and mt19937 - thing"

What is your compiler version?

Not all <random> implementations have a functioning std::random_device available. Visual Studio does, the MinGW version included with the latest Code::Blocks does not. The MinGW compiler with C::B is a a bit outdated.

Check the entropy of the std::random_device to see if you can use it or not. If you get a non-zero number it is functioning.
http://www.cplusplus.com/reference/random/random_device/entropy/

1
2
3
4
5
6
7
8
9
#include <iostream>
#include <random>

int main()
{
   std::random_device rd;

   std::cout << rd.entropy() << '\n';
}

With VS 2019 (Community) I get an output of 32. Code::Blocks 2003 it's '0'.

If std::random_device doesn't work for you seeding using a rather grotesque <chrono> clock statement does work:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <random>
#include <chrono>

int main()
{
   std::default_random_engine rd(static_cast<unsigned> (std::chrono::system_clock::now().time_since_epoch().count()));

   // OR

   // static unsigned seed { static_cast<unsigned> (std::chrono::system_clock::now().time_since_epoch().count()) };
   // std::default_random_engine rd(seed);

   std::uniform_int_distribution<int> dis(1, 100);

   for (size_t i { }; i < 25; i++)
   {
      std::cout << dis(rd) << ' ';
   }
   std::cout << '\n';
}


Why using srand/rand in a C++ program is not a good idea:

https://web.archive.org/web/20180123103235/http://cpp.indi.frih.net/blog/2014/12/the-bell-has-tolled-for-rand/

https://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful

I use CodeLite with the latest version of MinGW.

I already got an RNG working with this, just not "in a function" like this.

(Basically the "modern approach" that somebody posted in this topic: https://stackoverflow.com/questions/7560114/random-number-c-in-some-range)

So I assume that the compiler that I use "supports it" ...

By the way, I know that the srand or rand is not a good thing, I just used it to test if there was a problem with the random_device thing ....
Test std::random_device's entropy in CodeLite. I'd bet you get a return of 0.

Too bad the C++ Committee zebras never bothered instituting the "simple toolkit" proposed in a working paper to make using <random> (and <chrono>) less of a PITA.

http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3551.pdf

Creating a rudimentary "drop-in" header file makes using the C++ pseudo-random number generation and time utilities libraries almost as easy as using rand/srand.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/* A simple toolkit to help beginners using <random> library an easier task */

// 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
{
   static bool seeded { false };

   inline std::default_random_engine& urng()
   {
      static std::default_random_engine URNG { };

      return URNG;
   }

   inline void srand(bool FORCE_SEED = false)
   {
      static const 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;
      }
   }

   inline void srand(unsigned seed, bool FORCE_SEED = false)
   {
      // the URNG can't be reseeded unless forced
      if (!seeded || FORCE_SEED)
      {
         urng().seed(seed);

         seeded = true;
      }
   }

   // two function overloads to obtain uniform distribution ints and doubles
   inline int rand(int from, int to)
   {
      static std::uniform_int_distribution<> dist { };

      if (from > to) { throw std::invalid_argument("bad int params"); }

      return dist(urng(), decltype(dist)::param_type { from, to });
   }

   inline double rand(double from, double to)
   {
      static std::uniform_real_distribution<> dist { };

      if (from > to) { throw std::invalid_argument("bad double params"); }

      return dist(urng(), decltype(dist)::param_type { from, to });
   }

   // function for rolling dice, and checking if the # of pips is nonstandard
   inline int roll_die(int pips)
   {
      //check to see if the number of die pips is less than 2
      if (pips < 2)
      {
         return 0;
      }

      return rand(1, pips);
   }
}

#endif 

Dealing with a PRNG now is less stressful:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <array>
#include <iostream>
#include <numeric>
#include "random_toolkit.hpp"

int main()
{
   // "create" a random engine and randomize it
   rtk::srand();

   for (unsigned loop { }; loop < 50; loop++)
   {
      std::cout << rtk::roll_die(6) << ' ';

      if ((loop + 1) % 20 == 0)
      {
         std::cout << '\n';
      }
   }
   std::cout << "\n\n";

   // lambda to "flip a coin," returning a text representation of coin side
   auto flip_coin = [] () { return (rtk::rand(0, 1) ? "Heads" : "Tails"); };

   for (size_t loop { }; loop < 25; loop++)
   {
      std::cout << flip_coin() << '\t';

      if ((loop + 1) % 8 == 0)
      {
         std::cout << '\n';
      }
   }
   std::cout << '\n';
}

Wow!
Thank you for that!
That rtk:rand() function works perfectly!

Also, I will check out that PDF file that you linked to ...

Thanks again!
Using rand/srand is noob easy, <random> and <chrono> can be a bit harder. C++ random number generation can be intimidating for a beginner because there are so many choices of engines and distributions. C has rand. One function.

My toolkit is a work in progress, and I found a problem. Change the rtk::srand() function starting at line 39 to this:
39
40
41
42
43
44
45
46
47
48
   inline void 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;
      }
   }

Some error about 'more than one instance of overloaded function "rtk::srand" matches the argument list' when the first parameter is unsigned and you try to seed the engine with a number like 145 and no forced seeding. Ooops! rtk::srand(145);

With that said, look at the first rtk::srand function, line 24. Yow, that looks complicated, doesn't it?

Generating random number in C++ looks complicated because there are so many choices. With the toolkit you have the simplicity of C's approach with the robustness C++ offers.

Is it perfect? Hardly, but for most trivial needs it works. Generating random numbers in most instances should not be like trying to raise the dead with a long complicated ritual.
Topic archived. No new replies allowed.