Unknown problem

New to C++. I'm trying to understand RNG and how to produce random numbers in C++. I'm not sure what is the mistake of my code, but it gave me gibberish numbers of counts for each number, except one or two. Thanks for help

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
 #include  <iostream>
#include <ctime>
#include <random> 
void counting (int counter[], int num);
int main()
{   
    int counter[10];
    std::mt19937 gen(time(NULL)); 
    std::uniform_int_distribution <int> num(0,9);
    for (int k = 0; k < 100; k++)
    {   
        int temp = num(gen); 
        std::cout << temp << "\t" ; 
        counting(counter, temp); 
    }
    for(int k = 0; k < 10; k++)
    {
        std::cout << " Number " << k << " has appeared " << counter[k] << " times." <<  "\n"; 
    }


}
void counting (int counter[], int num)
{
    for(int k = 0; k < 10; k++)
    {
        if(num == k)
        {
            counter[k]++; 
        }
    }
    
}
The values of counter array aren't initialised to 0 - and their original value is indeterminate. Use

 
int counter[10] {};


Also, to initialise gen, you can use std::random_device {}() instead of time(NULL)

 
std::mt19937 gen(std::random_device {}());


Also, the counting function can be simplified to just ++counter[num].

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>

int main()
{
	const size_t nonums {100};
	const size_t maxno {10};

	int counter[maxno + 1] {};
	std::mt19937 gen(std::random_device {}());
	std::uniform_int_distribution <int> num(0, maxno);

	for (int k = 0; k < nonums; ++counter[num(gen)], ++k);

	size_t throws {};

	for (int k = 0; k <= maxno; throws += counter[k], ++k)
		std::cout << " Number " << k << " has appeared " << counter[k] << " times." << "\n";

	std::cout << throws << " throws\n";
}



 Number 0 has appeared 8 times.
 Number 1 has appeared 11 times.
 Number 2 has appeared 6 times.
 Number 3 has appeared 10 times.
 Number 4 has appeared 6 times.
 Number 5 has appeared 12 times.
 Number 6 has appeared 10 times.
 Number 7 has appeared 15 times.
 Number 8 has appeared 9 times.
 Number 9 has appeared 8 times.
 Number 10 has appeared 5 times.
100 throws

Last edited on
@seeplus

Thanks again seeplus. You have been very helpful.

Can you briefly explain the difference between using

 
std::mt19937 gen(std::random_device {}());


and using

 
std::mt19937 gen(time(NULL))


What is the use of random_device?

Thanks in advance.
Last edited on
random_device is just a C++ way to initialise mt19937 without having to resort to using time(). See http://www.cplusplus.com/reference/random/random_device/

Unless you're into serious statistical analysis, IMO:

1
2
std::mt19937 gen(std::random_device {}());
std::uniform_int_distribution <int> num(0, maxno);


is the incantation to copy/paste whenever random numbers within a range are required. Note that multiple different ranges can be defined. Eg

1
2
3
std::mt19937 gen(std::random_device {}());
std::uniform_int_distribution <int> num(0, maxno);
std::uniform_real_distribution<double> real(-10.0, 10.0);


If gen is required to be used in different functions, then I place it once at global level (IMO one of the few uses for a global variable). If a distribution is required only within a function, then they can be specified as static within that function. Eg:

1
2
3
4
5
6
int genrnd()
{
    static std::uniform_int_distribution <int> num(0, 999);

    return num(gen);
}

Last edited on
random_device doesn't work on quite a lot of systems (including cpp.sh).

From the reference on this site:
Notice that random devices may not always be available to produce random numbers (and in some systems, they may even never be available). This is signaled by throwing an exception derived from the standard exception on construction or when a number is requested with operator().

Unless the program really requires a stochastic process to generate random numbers, a portable program is encouraged to use an alternate pseudo-random number generator engine instead, or at least provide a recovery method for such exceptions.
Last edited on
:) Thanks for the update. I wasn't aware - as I always use MS VS and random_device works OK with that.
a software provided 'device' is no better than the other method, though: its doing the exact same kinds of things as the engines. the devices are for things like the lottery, which feeds something like a geiger counter to the software for the seed so no one can easily cheat the system.
Last edited on
> random_device doesn't work on quite a lot of systems

std::random_device as a random number generator that produces non-deterministic random numbers is currently available on all mainstream implementations.

Older versions of MinGW was a notable exception; it had a deterministic std::random_device
This appears to have been fixed now https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85494
I was pointing out that it didn't work on cpp.sh.

You can try that out. It throws an exception.
Last edited on
lastchance wrote:
random_device doesn't work on quite a lot of systems

I mentioned that in the past, that std::random_device isn't fully implemented with all compilers, and was "informed" I was wrong. It works with VS, it still doesn't work AFAIK with MinGW/GCC. I don't know about any other compiler since I really use only VS and MinGW/GCC.

If I want a time based seed for <random> I use the system clock in <chrono>, using the C library time function doesn't have the granularity.

Most times I seed using a seed sequence made from std::random_device and std::chrono::system_clock. The implementation details hidden in a header file simple toolkit, so I can obtain C++ random numbers almost as easily as using the C library by including a header file.

I found the idea for the toolkit in this C++ paper:
http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3551.pdf

C++ random number generation suffers from having too many options as well as implementation that is overly "wordy" compared to C.
Using chrono and random, one possibility could be:

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

auto rng {[]() {
	std::random_device seeder;

	return std::mt19937(static_cast<std::mt19937::result_type>(seeder.entropy() ? seeder() : std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())));
} ()};


int main()
{
	std::uniform_int_distribution<size_t> distrib(1, 100);

	for (int i = 0; i < 10; ++i)
		std::cout << distrib(rng) << ' ';

	std::cout << '\n';
}


which uses random_device() if available otherwise now().

I agree with Furry Guy re <random> is too wordy/complex for 'simple' requirement for random numbers. IMO <chrono> suffers the same.
> it still doesn't work AFAIK with MinGW/GCC

It does produce non-deterministic random numbers with current versions.
(Though the entropy that it reports is spurious).

This is what I get:

....@E431 MINGW64 ~
$ cat rd.cpp && g++ --version && g++ -std=c++20 -Wall -Wextra -pedantic-errors rd.cpp && ./a.exe && ./a.exe && ./a.exe && ./a.exe && ./a.exe
#include <iostream>
#include <random>
#include <iomanip>

int main()
{
    std::random_device rd{} ;
    for( int i = 0 ; i < 10 ; ++i ) std::cout << std::setw(12) << rd() << ' ' ;
    std::cout << '\n' ;
}

g++.exe (Rev6, Built by MSYS2 project) 10.2.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

  3539793305   1143445425   2317861138   1443013777   2365087591    115379409   1245968627   1424651833   3112829193    908711550
  3122665606    681391098   3580432448   3403446520   1976382215   3487341715   1815711815   1047479465   2650249767    739968122
  3479116007   2464914571   3509615043   2692243122   2958681922   1150317532   2498472056   1091511362   3305135777    375344081
   592724831   1254089846   2808203292   1550908971    791644958   2094468247   3530849434    126529277   3288776220    324979081
  2149172985    671824498   2536283940   4138701857   3956620448   1533105569   3678759274    837530881   1622355853   2689117420

....@E431 MINGW64 ~
$
Adapted from that C++ paper, using a seed sequence
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
#include <iostream>
#include <chrono>
#include <random>

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

   return URNG;
}

void srand()
{
   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));

   urng().seed(sseq);
}

int rand(int from, int to)
{
   static std::uniform_int_distribution<> dist { };

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

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

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

int main()
{
   // let's seed the random engine
   srand();

   // let's get some int random numbers
   for (int i { }; i < 10; ++i)
   {
      std::cout << rand(-10, 25) << ' ';
   }
   std::cout << "\n\n";

   // let's get some double random numbers
   for (int i { }; i < 10; ++i)
   {
      std::cout << rand(-1.5, 1.5) << '\n';
   }
}

Wrap those functions in a custom namespace, declare them inline and you have the beginnings a header-only simple random toolkit.
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_toolkit.hpp"

int main()
{
   // let's seed the random engine
   rtk::srand();

   // let's get some int random numbers
   for (int i { }; i < 10; ++i)
   {
      std::cout << rtk::rand(-10, 25) << ' ';
   }
   std::cout << "\n\n";

   // let's get some double random numbers
   for (int i { }; i < 10; ++i)
   {
      std::cout << rtk::rand(-1.5, 1.5) << '\n';
   }
}

Now I don't have to deal with the messy details of implementation if I want to generate random numbers "The C++ way." No matter the compiler and issues with std::random_device. I include my custom header and away we go!

There are other details of my custom header file that are for a different discussion.
@seeplus, don't use to_time_t()! Use time_since_epoch() instead. It probably has much more precision.

 
auto seed = std::chrono::system_clock::now().time_since_epoch().count();

'Simple random number generation facilities' in Library Fundamentals v2 ( ISO/IEC TS 19568:2017)
https://en.cppreference.com/w/cpp/experimental/lib_extensions_2#Simple_random_number_generation_facilities
@dutch. Thanks. Noted.
Re 'Simple random number generation facilities' in Library Fundamentals v2 ( ISO/IEC TS 19568:2017)


These don't seem to have been implemented in VS 2019 as the folder experimental/random is not present. Also MSDN documentation does not seem to refer to randint() or reseed(). Ahhh...
Topic archived. No new replies allowed.