Random numbers

Pages: 12
Hello. After reading a few threads for generating random numbers, I discovered that this is the right way to use it:

1
2
3
4
5
6
7
8
9
10
#include<iostream>
#include<cstdlib>
#include <ctime>
using namespace std;
int main()
{
srand(time(0));


}

But why do I need these libraries and why do I need to call this function? How does it works?

Also, what's the right way to set ranges? I don't get this example: http://www.cplusplus.com/reference/cstdlib/rand/.
Last edited on
I want to start by saying you should probably be using C++11 random functions rather than srand/rand. srand/rand are kind of crappy.

But that aside:

But why do I need these libraries


srand() exists inside the cstdlib header. Without it, the compiler has no idea what you're trying to do when you call it.

time() exists inside the ctime header. Ditto.

why do I need to call this function? How does it works?


Pseudo-random number generators are basically mathematical equations. You can think of it like this:

- They take an input number X
- They run that number through a crazy math formula to produce another number Y
- Y is your generated random number
- Y is then used as the next 'X' the next time you are to generate a number.

So each time you call rand() to get a new number, the generator has new input, which means you get new output.


What srand does... is it "seeds" the RNG -- IE: it gives it a starting point (that initial input). Without seeding, I believe srand starts with a seed of 0. So you'll still get random numbers from rand(), but they will be the same sequence of numbers each time you run the program because they have the same starting point.

By giving time() to srand(), you are seeding the RNG with the current time. Since the current time changes every second, this means that each time the user starts the program, the time is different, and therefore the RNG is seeded with a different number, and therefore you'll get a different sequence each time.


Also, what's the right way to set ranges?


With rand(), the best practical way is to just use the mod operator (%).

Mod gives you the remainder after a division. So... 7%3 would be 1, because 7/3 is 2 remainder 1.

This is useful because any number divided by X is going to have a remainder that falls between [0,X). IE, foo % 3 is going to be either 0,1, or 2... regardless of what 'foo' is.

So with rand(), this effectively creates a range:
1
2
3
4
5
6
7
8
9
10
int x = rand() % 5; // x will be 0,1,2,3, or 4

// From there, if you want to adjust where the range starts, you can just offset it by adding:
x += 2;  // 0,1,2,3,4  now becomes  2,3,4,5,6

// So if you want a random number between [5,12) ... you could do this:
x = (rand()%7) + 5;

// %7 gives you [0,7)
// +5 gives you [5,12) 
Thank you.
Is the default_random_engine (http://www.cplusplus.com/reference/random/default_random_engine/) more reliable? I don't get how to use it.
Last edited on
closed account (SECMoG1T)
according to my experience with default_random_engine i don't think it is the best i wouldn't recommend it, i would rather use the mersenne twister http://www.cplusplus.com/reference/random/mt19937/ it's my favorite
I think I need something more easier to use.
I guess you can use srand in that case, Watch this video to know how it works -

https://www.youtube.com/watch?v=naXUIEAIt4U&list=PLAE85DE8440AA6B83&index=27&ab_channel=thenewboston

Or just look it up somewhere
closed account (SECMoG1T)
their usage is quite easy for example let's make a simple function that returns a random int with a uniform distribution in the range 1-100.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 #include <random>
 #include <ctime>
  #include <iostream>

  unsigned int rand_no()
    {
        static std::mt19937 mt_rand_gen(std::time(nullptr));
        static std::uniform_int_distribution<unsigned int> dist(1,100);

        return dist(mt_rand_gen);
    }

   int main()
   {
         auto x=rand_no();
         std::cout<<x<<std::endl;
    }
an easy example of MT:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
#include <ctime>
#include <random>

int main ()
{
	std::mt19937 gen (time(0)); // mt19937 is a standard mersenne_twister_engine
  
	for(int i=0; i<100; i++)
		std::cout << gen()%100 +1 << std::endl; // randomly value: 1 to 100


return 0;
}
Don't use % with C++11 RNGs. Use standard distributions like in andy1992's example
(uniform_int_distribution, etc).
@Andy or anyone else really. Why is it that you think the default_random_engine is not the best, and why you prefer using mt19937? Is it better, how do they work exactly?
I prefer to use mt19937 for determinism. If I want to have reproducible results, all I have to do is supply the same seed to a deterministic generator and I'll get the same string of numbers every time.

But since default_random_engine can change, there's no guarantee I'll have that ability if I use it.
So you would use default_random_engine the same way you would use the srand and rand?
> So you would use default_random_engine the same way you would use the srand and rand?

No. Typically, we would use a distribution to post-process the sequence of numbers generated by the engine.

std::default_random_engine is a type alias for a standard engine chosen by the implementation ( "on the basis of performance, size, quality, or any combination of such factors, so as to provide at least acceptable engine behaviour for relatively casual, inexpert, and/or lightweight use" - IS).

With std::default_random_engine, we would get reproducible results only on the same implementation.

For standard distributions like std::uniform_int_distribution or std::normal_distribution there is no guarantee of consistently reproducible results across implementations.

To get reproducible random numbers (across implementations, when distributions are involved), in addition to using a specific engine like std::mt19937, we also need to write custom random number distributions to post-processes the output of the random number engine.
Thanks for your response. I started using the c++11 stuff yesterday, and I went for the random header, and the chrono one, to act as the seed. Here is a the code

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
std::uniform_int_distribution<int> uni(1, 100);
	int seed = std::chrono::steady_clock::now().time_since_epoch().count();
	std::default_random_engine dre(seed);

	
	int guess = 0;
	int amountOfGuesses = 0;

	std::cout << "Pick a number for the computer to guess  ";
	std::cin >> guess;

	do
	{
		

		if (uni(dre) < guess )
			std::cout << "Too low!" << std::endl;
		else if (uni(dre) > guess)
			std::cout << "Too high!" << std::endl;
                       
                   amountOfGuesses++;

		if (amountOfGuesses == 7)
			guess = uni(dre);

	} while (uni(dre) != guess);
	
	std::cout << "You guessed right!" << std::endl;
	std::cout << "You guessed " << amountOfGuesses << " Times!" << std::endl;
	system("pause");


I never finished it etc, but you get the idea of how I implemented the randomness. Could you tweak the code a bit in a way to show the other/better ways of producing/reproducing random numbers?
Last edited on
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <random>
#include <chrono>

int random_int( int minv, int maxv )
{
    static std::default_random_engine dre( std::chrono::high_resolution_clock::now().time_since_epoch().count() ) ;
    return std::uniform_int_distribution<int>( minv, maxv )(dre) ;
}

int main()
{
    int number = random_int( 1, 100 ) ;

    // ask the user to guess what this number is
}
Thank you :)
You can try this if you get some conversion warnings

 
srand(unsigned(time(NULL)));
@JLBorges, we used to write srand() only once. do we have to write line 7 in your code, for every function?
Pages: 12