1

Firstly, sorry if it's a silly question, but I have recently learn how to deal with classes. My problem is that I've created a constructor which gives every data member a random number, with the following syntax:

#include <random>

...

//Constructor
cSystem::cSystem(void)
{
//Range of random numbers
double lower_bound = -100;
double upper_bound = 100;
std::uniform_real_distribution<double> unif(lower_bound,upper_bound);
std::default_random_engine re;

/*Initialization of data members*/
sMass=1.0014;
sPositionX= unif(re);
sPositionY= unif(re);
}

But then, I want to create an array of these "systems" in my main function

int main (void)
{
cSystem systems[numsist]; //numsist is a constant defined to be 1000

//Function that writes its characteristics in a file using a method that returns positions
getPositions(systems);

return 0;
}

But in the file, I just get

-73.6924    -8.26997
-73.6924    -8.26997
-73.6924    -8.26997

I thought that I was going through the constructor for every element of the array, giving it a different random value, but it seems like a single value is given at the beginning and then it is repeated throughout the vector. What can I do to get a constructor that initializes every element in a random way?

Note: I also get the same values everytime I run the program, I suppose that the seed does not change, why is that the case?

2 Answers2

0

You get the same values of sPositionX and sPositionY for each instance of cSystem since you are creating the std::uniform_real_distribution<double> and the std::default_random_engine every time the constructor is called.

You need to create them once and reuse them in the constructor. One way to do that would be to create class scoped static objects.

struct cSystem
{
   ...

   static std::uniform_real_distribution<double> unif;
   static std::default_random_engine re;
};

and just use them in the constructor.

Here's a complete program.

#include <random>
#include <iostream>

struct cSystem
{
   cSystem();

   double sPositionX;
   double sPositionY;
   double sMass;

   static std::uniform_real_distribution<double> unif;
   static std::default_random_engine re;
};

std::uniform_real_distribution<double> cSystem::unif(-100, 100);
std::default_random_engine cSystem::re;

//Constructor
cSystem::cSystem()
{
   /*Initialization of data members*/
   sMass=1.0014;
   sPositionX= unif(re);
   sPositionY= unif(re);
}

void getPositions(cSystem* systems)
{
   for (unsigned i = 0; i < 3; ++i )
   {
      std::cout << systems[i].sPositionX << " " << systems[i].sPositionY << std::endl;
   }
}

int main (void)
{
   cSystem systems[3];

   getPositions(systems);

   return 0;
}

See it working at https://ideone.com/bS8E4u.

Update, in response to OP's comment.

You can initialize re as shown below to get different random numbers when the program is run again and again.

std::default_random_engine cSystem::re{std::random_device()()};
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • It solves the problem of having the same number all over the array, which I'm very thankful for. However, the seed doesn't seem to change (as I mentioned in the "Note" part of my question), because I get the same values everytime I run the program. This program is a simulation of a galaxy, therefore, I would want to have different random initial positions of the solar systems in every run. I don't know how to use very well, but maybe I could use srand(time(NULL)) and insert it into the seed that uses, or maybe there's a not-so-intricate solution. – Adri Escañuela Mar 27 '20 at 13:17
0

My problem is that I've created a constructor which gives every data member a random number

  • You are not seeding the PRNG when you make it default constructed. It will start with its default state every time.
  • The PRNG is not supposed to be owned by any one class. It's expensive to seed it, and it is supposed to be shared by all functions (per thread) that needs to generate random numbers - so separate it from any classes.

I perfer to select a certain PRNG, but with the default, it could look like this:

std::default_random_engine& prng() {
    // seed only one PRNG per thread that needs is:
    static thread_local std::default_random_engine gen(std::random_device{}());
    return gen;
}

In every distribution function (member function or otherwise) you use in your program you may now call prng() to get the generator, seeded once and not overseeded.

Ted Lyngmo
  • 93,841
  • 5
  • 60
  • 108
  • It solves the problem of having the same number all over the array, which I'm very thankful for. However, the seed doesn't seem to change (as I mentioned in the "Note" part of my question), because I get the same values everytime I run the program. This program is a simulation of a galaxy, therefore, I would want to have different random initial positions of the solar systems in every run. I don't know how to use very well, but maybe I could use srand(time(NULL)) and insert it into the seed that uses, or maybe there's a not-so-intricate solution. – Adri Escañuela Mar 27 '20 at 13:18
  • @A.D.EscañuelaCopado If the seed doesn't change (you can test it by printing it), you may need to add entropy yourself. Here's a half-hearted version: [portable seeding if buggy `random_device`s are taken into account](https://stackoverflow.com/a/60723456/7582247). The third bullet in the second note applies to your case. – Ted Lyngmo Mar 27 '20 at 13:34
  • ... and no, I recommend that you don't go with `srand(time(NULL))`. You are on the right track with using ``. It's better in every aspect. – Ted Lyngmo Mar 27 '20 at 13:42
  • @A.D.EscañuelaCopado You didn't actually test the example `prng()` in my answer, did you? I just noticed that you commented the same thing to the other answer, which does not contain any seeding at all, while mine does. – Ted Lyngmo Mar 27 '20 at 14:58
  • I tested both, and both "work" except for the change in seed, now I've updated my compiler to the latest version, in case that _that_ is the problem, since you said that "older versions of MinGW had a buggy implementation" in the post that you mentioned. However, I keep getting the same values, even for GCC 9.3.0 and using the c++17 flag. – Adri Escañuela Mar 27 '20 at 16:51
  • I also tried the correction that you inserted in the [portable seeding if buggy random_devices are taken into account](https://stackoverflow.com/questions/60721093/random-number-from-normal-distribution-in-c/60723456#60723456) post, but an error comes up: `'size' is not a member of 'std'`, and it happens in the `for(size_t s = 0; s < std::size(si);) {` line. – Adri Escañuela Mar 27 '20 at 16:54
  • `std::size` is a C++17 function template. If you are using an older version, just replace `std::size` with the [old](https://stackoverflow.com/a/4108340/7582247) way of calculating the extent of arrays or use `std::extent::value`. Why not use C++17? – Ted Lyngmo Mar 27 '20 at 17:40
  • When using my example that is compensating for buggy MinGW implementations you can't possibly get the same seed every time - unless your `steady_clock` is broken too. – Ted Lyngmo Mar 27 '20 at 17:44
  • Do `std::cout << prng()() << '\n' << prng()() << '\n';` Are the values the same every time? – Ted Lyngmo Mar 27 '20 at 17:48
  • 1
    I'm using CodeBlocks as my IDE in Windows 10 and I've implemented C++17, but I probably haven't done it correctly, that's why `std::size` doesn't work. I used to use Linux command line but my computer broke, so that's the only thing available right now. Regarding the `cout` of `prng()()`, I get a string of different values, but the string is the same everytime I run it. I'm happy to tell you that using `(sizeof(si)/sizeof(*si))` or `std::extent::value` makes it work :D. **Thank you a thousand times** – Adri Escañuela Mar 27 '20 at 18:32