I cant create a "random" number generator.

Sep 8, 2019 at 9:14am
So I'm quite new to c++ and making a Rock Paper Scissors game. First I made it with srand and rand. It worked fine. But when I tried to them with c++11's <random> features it had errors. And the error is that it ALWAYS gives me 1. I lost count after about 20 attempts.

so herers my main().
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

#include <iostream>
#include <random>
using namespace std;
int randomNumberGenerator();
int winnerDecider(int, int);
void computerDescisionPrinter(int);

int main(){
    //Ive skipped a bunch of cout here.
    int userChoice;
    cin >> userChoice;
    cout << "You choose " << userChoice << endl;
    int computerChoose = randomNumberGenerator();
    computerDescisionPrinter(computerChoose);
    cout << (winnerDecider(userChoice,computerChoose)?"You win":"You loose") << endl;
    return 0;
}

//now there are a few functions
//but this is the only one in question.
int randomNumberGenerator(){
    default_random_engine engine{static_cast<unsigned int>((time(0)))};
    uniform_int_distribution<unsigned int> randomInt{1,3};
    {int c = randomInt(engine);
    cout << c << " -the random number" << endl; //this is to see what is the random number. later ill return it without storing in c.
    return c;
    }         
}

output:
Choose one of the following:
1. Scissors
2. Paper
3. Rock
1
You choose 1
1 -the random number
Computer choose 1, Scissors
Choose one of the following:
1. Scissors
2. Paper
3. Rock
1
You choose 1
1 -the random number
............ it goes on.

an example where I choose 2 or 3.
Choose one of the following:
1. Scissors
2. Paper
3. Rock
2
You choose 2
1 -the random number
Computer choose 1, Scissors
You loose
Last edited on Sep 8, 2019 at 9:55am
Sep 8, 2019 at 9:43am
PLEASE learn to use code tags, it makes reading and commenting on your source MUCH easier.

HINT, you can edit your post and add the tags:

http://www.cplusplus.com/articles/jEywvCM9/

uniform_int_distribution<unsigned int> randomInt(1,3);

http://www.cplusplus.com/reference/random/uniform_int_distribution/uniform_int_distribution/

If you are going to use <random> you should use <chrono> to get a time seed for your PRNG engine.

Sep 8, 2019 at 11:52am
Hello a111poudel,

Along with Furry Guy's link I found this video worth watching:
https://channel9.msdn.com/Events/GoingNative/2013/rand-Considered-Harmful

This will explain all the problems of using "rand()" and give an alternative for using "rand()".

Also "default_random_engine" is a type def for the "mt19937" RNG. If I have said that correctly.

Hope that helps,

Andy
Sep 8, 2019 at 1:17pm
I think I got it but still have a question to be answered.
So my mistake was to initialize default_random_engine and uniform_int_distributor inside the function. That way every time the function randomNumberGenerator got invoked it all reset. And every time the function was called it was rest. So I just took them out of function just used randomInt(engine); inside it.

But my question still remains. If I am passing time(0), whoose value changes every second shouldnt it still work even if I reinitialize it?
Sep 8, 2019 at 2:56pm
Hello a111poudel,

There are some things in C++ that only need to be done once in a program. Like:
srand(static_cast<size_t>(time(nullptr)));,
std::default_random_engine engine{ seed }; and
std::cout << std::fixed << std::showpoint << std::setprecision(2);. These are usually done near the beginning of "main".

The way "srand()" and "rand" work is "srand()" sets up the start of generating a random number and each call to "rand" uses the previously generated random number to create the next, as I understand it. Each time you call "srand()" , say in a function, you may be giving a new number to the RNG, but you are essentially starting over and the RNG does not have the chance to use its function(s) to create a new random number. Also a function can be called quickly enough so that the value returned from the call to "time()" could be the same number thus you could generate the same random number two or more times before it changes.

In the above line of code for "srand()" notice the use of "nullptr". Although (0) zero and "NULL" will work I read some time back, and still trying to find where, that "nullptr" is the best choice when you want "time()" to return a number.


If I am passing time(0), whose value changes every second


Not really when you consider that the computer can execute many instructions in the time of milliseconds. A call to "time()" could happen many times before "time()" would return a new value. Thus you could be giving "srand()" the same number to start with and if you look at a greater range of numbers, say 100, you would see the same numbers several times before they would change.

From http://www.cplusplus.com/reference/cstdlib/srand/?kw=srand

For every different seed value used in a call to srand, the pseudo-random number generator can be expected to generate a different succession of results in the subsequent calls to rand.

Two different initializations with the same seed will generate the same succession of results in subsequent calls to rand.

Hope that helps,

Andy

Sep 8, 2019 at 3:20pm
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
#include <iostream>
#include <random>
#include <ctime>
#include <iterator>

int random_number( int min_value = 1, int max_value = 3 )
{
    // static: https://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables
    // these are initialized only once; the first time when the function is called
    // We use a seed sequence with two values; this takes care of implementations (MinGW)
    // where std::random_device is spurious
    static const std::size_t seeds[] { std::random_device{}(), std::size_t( std::time(nullptr) ) } ;
    static std::seed_seq sseq( std::begin(seeds), std::end(seeds) ) ;
    static std::mt19937 engine(sseq) ;

    return std::uniform_int_distribution<int>{ min_value, max_value }(engine) ;
}

int main()
{
    for( int i = 0 ; i < 10 ; ++i ) std::cout << random_number() << ' ' ;
    std::cout << '\n' ;

    for( int i = 0 ; i < 10 ; ++i ) std::cout << random_number( 25, 72 ) << ' ' ;
    std::cout << '\n' ;
}

http://coliru.stacked-crooked.com/a/341582c72154f16a
https://rextester.com/BTMCW4846
Sep 8, 2019 at 3:52pm
Also "default_random_engine" is a type def for the "mt19937" RNG. If I have said that correctly.

Not true.

What actual generator is used for std::default_random_engine is up to the implementation/vendor, it is NOT necessarily an alias for std::mt19937. The only requirement of the C++ standard is to provide acceptable engine behavior for relatively casual, inexpert, and/or lightweight use.

http://www.cplusplus.com/reference/random/default_random_engine/

If you don't care what generator is used, std::default_random_engine is as good a choice as the others.

If I am passing time(0)

What if someone runs your program multiple times during that second. Then the pseudo-random number generator is seeded with the same seed, so it generates the same sequence of random numbers.

Using the system clock in <chrono> can get time intervals in the millisecond range, much finer control than what the C library time function can provide.

Another view of why using the C library for random numbers 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/

An old C++ standard work-group paper(?) on random number generation in C++11:

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

So my mistake was to initialize default_random_engine and uniform_int_distributor inside the function.

You didn't give the object created in the function static duration, static duration would have created the object only once no matter how many times you entered the function.

https://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables

A function scope variable created with static duration exists between function calls, the value is retained.

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

void my_func();

int main()
{
   // let's call the function several times
   my_func(); my_func(); my_func();  my_func(); my_func(); my_func();
}

void my_func()
{
          int a = 0;
   static int b = 0;

   a++; b++;

   std::cout << "a: " << a << ", b: " << b << '\n';
}

a: 1, b: 1
a: 1, b: 2
a: 1, b: 3
a: 1, b: 4
a: 1, b: 5
a: 1, b: 6


<edited to fix a rather silly typo>
Last edited on Sep 8, 2019 at 4:58pm
Sep 8, 2019 at 4:47pm
@Furry Guy,

Thank you for the input. I understand what you mean and will make note of it for the future.

Andy
Sep 8, 2019 at 5:15pm
@ Handy Andy,

Look at the cplusplus page for std::default_random_device, it deals with using a linear_congruential_engine type, not a mersenne_twister_engine type.

http://www.cplusplus.com/reference/random/default_random_engine/

The constructor example goes one step further and uses a specific engine instantiation: std::minstd_rand0

http://www.cplusplus.com/reference/random/linear_congruential_engine/linear_congruential_engine/

@a111poudel,

I'd bet you are feeling a bit overwhelmed at the moment, this is a lot of new material to chew on. A tutorial on random number generation might help ease the confusion/pain:

https://www.learncpp.com/cpp-tutorial/59-random-number-generation/
Last edited on Sep 8, 2019 at 5:16pm
Topic archived. No new replies allowed.