1

I'm writing code for initializing particle positions in a phase space using a density function. I would like to randomly sample their locations according to an arbitrary distribution, and the easiest way to do this that I found was to make a small class with everything needed to sample according to an array of probabilities that I give it. However, I've found that I get the same sequence of numbers every time I run the code.

vose *sampler = new vose(density, init_grid_count);

for (unsigned int i=0; i<20; i++)
{
    unsigned int index = sampler->alias_method();
    cout << index << '\t';
}

This is the definition of the function I use

unsigned int vose::alias_method()
{
    double x = uniform(mt);

    unsigned int i = floor(array_size*x)+1;
    double y = array_size*x+1-i;

    if (y<distribution[i])
        return i;

    else
        return alias[i];
}

I found that if I do the same loop in the constructor for the class so that it runs when I initialize the vose object, it behaves as expected. This means that it generates a different random sequence each time. So why is it that when I do this in the constructor it works right, but when I use it in a member function it outputs the same sequence every time?

The problem is not that my compiler doesn't support the seed generation. I'm using the <chrono> library and when I print out the seed in the constructor, it is different every time. In case you think there could be something off in the constructor, here it is.

vose::vose(double *array, unsigned int size)
{
    //Initialize
    auto seed = chrono::high_resolution_clock::now().time_since_epoch().count();
mt19937 mt(seed); // Seeds Mersenne Twister with Device RNG

    uniform_real_distribution<double> uniform(0, 1);

    distribution = array;
    array_size = size;

    alias = new unsigned int[size];
    for (unsigned int i = 0; i<size; i++)
    {
        alias[i] = i+1;
    }
}

Edit: To be perfectly clear, everything works right when I run the method in the constructor. However, that's obviously not where I mean ultimately to run it so I need to figure out how to have a method run in the main section of code.

Edit: In response to the possibility that I hid the generator or something, here is the class declaration in the header file.

class vose
{
public:
    vose(double*, unsigned int);
    void demo();
    unsigned int alias_method();

private:
    std::mt19937 mt;

    // I struggled with this for a while, you don't give it args
    // until you use it since it's not a function, it's a strange class
    std::uniform_real_distribution<double> uniform;

    double *distribution;
    unsigned int array_size;
    unsigned int *alias;
};
Liam Clink
  • 189
  • 1
  • 11
  • This is not the same as that question you linked to. I do seed the RNG only once, and that was the mistake on that post. The _critical_ and _novel_ (I think) problem here is that it works in the constructor but not in a method of the class. – Liam Clink Jan 18 '18 at 17:57
  • I have shown this answer to similar questions regarding `` & `` this wrapper class may be of some help to you: https://stackoverflow.com/a/48115400/1757805 – Francis Cugler Jan 18 '18 at 17:58
  • 1
    That won't work for my purposes because it isn't amenable to using an array to define a distribution. It does seem convenient though and I'll use it in future. – Liam Clink Jan 18 '18 at 18:04
  • Your example doesn't make sense to me; inside the `vose` constructor, you aren't actually using `mt` or `uniform` anywhere. Are `mt` and `uniform` supposed to be members of `vose`? If so, you are *hiding* those members inside the constructor by declaring local variables with the same names, which means your seed is not being applied. If that's not the issue, can you post the definition of the `vose` class? – 0x5453 Jan 18 '18 at 19:54
  • You might be onto something. I wasn't sure how to initialize the stuff so I might have done something stupid. – Liam Clink Jan 18 '18 at 20:51
  • @Mr.HelloBye The best way is typically to use the [contructor initializer list](http://en.cppreference.com/w/cpp/language/initializer_list) or a [default member initializer](http://en.cppreference.com/w/cpp/language/data_members#Member_initialization). If those won't work, rewrite the statements in the constructor to have the form: `mt = std::mt19937(seed);`. This assigns to the class member `mt` instead of creating a new local variable named `mt`. (Though the other two methods should still be preferred for performance reasons.) – 0x5453 Jan 19 '18 at 15:52
  • Yup, 0x5453, that worked. Thank you so much! I realized after messing around with it a lot that the initial state of the RNG was being saved, perhaps using some kind of default seed to make clear that the RNG isn't working right for debugging. If you would leave this as an answer so I can mark it and upvote that would be great. – Liam Clink Jan 21 '18 at 20:28

0 Answers0