1

I'm a C++ beginner coming from a Java and C# background. I'm trying to use the same default_random_engine and normal_distribution<double> at every creation of a new object. Before I was using a new default_random_engine with a new seed and a new normal_distribution<double> in every constructor. I think that way the normal_distribution doesn't work correctly.

Old code ->

my_object.cpp:

default_random_engine generator;

MyObject() {
    double mean = 1.0;
    double std = 0.5;
    normal_distribution<double> distribution(mean, std);
    QTime time = QTime::currentTime();
    uint milli = (time.hour() * 60 * 60 * 1000) + (time.minute() * 60 * 1000) + (time.second() * 1000) + time.msec();
    generator.seed(milli);
    myValue = distribution(generator);
}

This compiled and the values for myValue were randomly distributed. I just think they didn't match the normal distribution, because I always created a new default_random_engine and normal_distribution and used a new seed.

My new code ->

main.h:

class Main
{
   public:
       static default_random_engine generator;
       static normal_distribution<double> distribution;
};

main.cpp:

default_random_engine generator;
normal_distribution<double> distribution;

int main(int argc, char *argv[]) {
    double mean = 1.0;
    double std = 0.5;
    distribution(mean, std);
    QTime time = QTime::currentTime();
    uint milli = (time.hour() * 60 * 60 * 1000) + (time.minute() * 60 * 1000) + (time.second() * 1000) + time.msec();
    generator.seed(milli);
}

my_object.cpp:

default_random_engine generator;
normal_distribution<double> distribution;

MyObject() {
    myValue = distribution(generator);
}

But now I get 10 errors on compile time:

error C2228: left of '.min' must have class/struct/union
error C2780: '_Rty std::_Nrand(_Engine &,long double,_Rty)' : expects 3 arguments - 2 provided
error C2780: '_Rty std::_Nrand(_Engine &,double,_Rty)' : expects 3 arguments - 2 provided
...

What am I doing wrong? Why am I getting the errors? And am I correct that earlier my normal distribution/random generation wasn't correct? Will I be able to produce the wanted normal distribution this way?

Praetorian
  • 106,671
  • 19
  • 240
  • 328
John
  • 909
  • 4
  • 12
  • 29
  • I got rid of the error with "distribution.param(std::normal_distribution(mean,std).param())". I just don't understand why it worked with "distribution(mean, std)" in my old code. – John May 29 '15 at 11:55
  • I posted an answer to my question, I guess the code counts as SSCCE. – John May 29 '15 at 12:07
  • In your old code you were initializing a new `normal_distribution` object - `normal_distribution distribution(mean, std);` calls the constructor. In the new code, you've already constructed a global `normal_distribution` instance, then `distribution(mean, std);` calls the [`operator()`](http://en.cppreference.com/w/cpp/numeric/random/normal_distribution/operator%28%29) overload, but with the wrong argument types, causing the error. – Praetorian May 29 '15 at 12:21
  • Thanks a lot for that explanation! Now I understand why it didn't work. – John May 29 '15 at 13:36

2 Answers2

3

One of the issues is

distribution(mean, std);

This line does not do what you think. In order to set the parameters of the distribution, use std::normal_distribution<>::param() function like

distribution.param(std::normal_distribution<double>(mean,std).param());

or (thanks @Praetorian)

distribution.param(decltype(distribution)::param_type(mean, std));

Also, it looks like your distribution is a static object inside class Main, but you then define another one in the .cpp file. If you just want to ODR the first one, use

normal_distribution<double> Main::distribution;

instead, and subsequently use Main::distribution instead of distribution. Same for the engine.

vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • You're supposed to be able to construct a `param_type` using the same arguments as the distribution's constructor. So something like this should work too - `distribution.param(decltype(distribution)::param_type(mean, std));` – Praetorian May 28 '15 at 22:44
  • @Praetorian thanks, didn't know this shortcut. It works indeed, just tested. – vsoftco May 28 '15 at 22:46
2

I think I got the solution to my problem with the help of the other answers here. I create and initialize the default_random_engine, normal_distribution and random_device in the "Main" and use them in "MyClass" with the extern keyword. If I'm not wrong this way every time a new "MyClass" object is created it should use the same random generators, so that I should get the normal distribution I want to get.

Main.cpp

#include "Main.h"
#include "MyClass.h"

#include <iostream>
#include <random>

using namespace std;

normal_distribution<double> distribution;
default_random_engine engine;
random_device rd;

int main(int argc, char *argv[]) {
    double mean = 1.0;
    double std = 0.5;
    distribution.param(std::normal_distribution<double>(mean, std).param());
    engine.seed(rd());
    MyClass obj = MyClass();        
    return 0;
}

MyClass.h:

#pragma once
class MyClass
{
public:
    MyClass();
    double value;
};

MyClass.cpp:

#include "MyClass.h"

#include <random>

using namespace std;

extern default_random_engine engine;
extern normal_distribution<double> distribution;

MyClass::MyClass()
{
    value = distribution(engine);
}
John
  • 909
  • 4
  • 12
  • 29
  • Unless you really need a global `random_device` instance, I'd get rid of that, and use `engine.seed(std::random_device{}());` You should also [stop](https://stackoverflow.com/q/1452721/241631) using `using namespace std;` – Praetorian May 29 '15 at 12:24
  • You're right, I only need the random_device once, so I could do it your way. And thanks for the advice with "namespace std". – John May 29 '15 at 13:25