C++ mt19937 getting the same sequence multiple times

156 views Asked by At

I'm trying to create a deterministic hash generator using a pseudo random number sequence with the mt19937 engine in the <random> library. Therefore, I need to get the same sequence of numbers every time I feed the engine with the same seed.

My code looks like this:

#include <iostream>
#include <random>
#include <time.h>
#include <Windows.h>

int randomnumber = 0;

int main(){
    std::random_device rd;
    std::uniform_int_distribution<int> dist(0, 1);

    std::mt19937(123);
    for (int i = 0; i < 32; i++) {
        randomnumber |= ((unsigned)(dist(rd)) << i);
    }
    std::cout << (unsigned int)randomnumber << std::endl;

    std::mt19937(112);
    for (int i = 0; i < 32; i++) {
        randomnumber |= ((unsigned)(dist(rd)) << i);
    }
    std::cout << (unsigned int)randomnumber << std::endl;

    std::mt19937(123);
    for (int i = 0; i < 32; i++) {
        randomnumber |= ((unsigned)(dist(rd)) << i);
    }
    std::cout << (unsigned int)randomnumber << std::endl;

    return 0;
}

I tried setting the seed every time I generated a new hash, but the random number sequence was still different.

The outputs are:

1126954929
3745251263
3753639871
4

There are 4 answers

4
Pizza Monkey On

Thank you for your answeres, i use srand() and rand() now and reset the randomnumber to 0 everytime.

0
Severin Pappadeux On

You need custom seed sequence object to fully control std::mt19937, because its internal state is 624 uint32_t;

Along the lines (UNTESTED! Just a sketch!)

struct mt19937_seed_seq
{
    public: using result_type = uint32_t;
    
    public: const size_t state_size = 624;

    private: result_type _state[state_size];

    public: mt19937_seed_seq() {
        for(int k = 0; k != state_size; ++k)
            _state = k; // put whatever initialization you want here
    }

    public: inline size_t size() const {
        return state_size;
    }

    public: template <class OutputIterator> void param (OutputIterator dest) const {
        for(auto k = 0; k != state_size; ++k) {
            *dest++ = _state[k];
        }
    }

    public: template <class RandomAccessIterator>  void generate (RandomAccessIterator first, RandomAccesIterator last) {
        int k = 0;
        while (first != last) {
            *first++ = _state[k];
            if (++k == state_size)
                break;
        }
    }
};

So doing initialization

mt19937_seed_seq sseq{};
std::mt19937 mtr{sseq};

should provide you with fully controlled rn generator;

0
M.M On

std::mt19937(123); creates and then destroys an unnamed object (like doing int(5);)

Your code just gets numbers from rd without any reference to a mt19937 object .

To get numbers from a mt19937 you need to actually create and use an instance of it, e.g.:

std::mt19937 m1(123);
for (int i = 0; i < 32; i++) {
    randomnumber |= ((unsigned)(dist(m1)) << i);

If you are using fixed seeds and don't want "hardware" randomness then the std::random_device is unnecessary.

0
Adam On

Your code is very close, but there are two reasons why it didn't do what you expect it to.

First, this code std::mt19937(123); does not seed some global RNG. It creates a seeded rng object and then never uses it. The actual RNG you're using is std::random_device rd;, which is not being seeded at all. This means the value is not only unpredictable but also one iteration influences the RNG of the subsequent iterations.

Second, you're reusing a global randomnumber and then not setting it back to 0 between iterations. So the value of the first iteration influences the second, etc.

I've slightly tweaked your code to fix these issues:

#include <iostream>
#include <random>

int main(){
  std::uniform_int_distribution<int> dist(0, 1);

  {
    std::mt19937 rd(123);
    int randomnumber = 0;
    for (int i = 0; i < 32; i++) {
      randomnumber |= ((unsigned)(dist(rd)) << i);
    }
    std::cout << (unsigned int)randomnumber << std::endl;
  }

  {
    std::mt19937 rd(112);
    int randomnumber = 0;
    for (int i = 0; i < 32; i++) {
      randomnumber |= ((unsigned)(dist(rd)) << i);
    }
    std::cout << (unsigned int)randomnumber << std::endl;
  }

  {
    std::mt19937 rd(123);
    int randomnumber = 0;
    for (int i = 0; i < 32; i++) {
      randomnumber |= ((unsigned)(dist(rd)) << i);
    }
    std::cout << (unsigned int)randomnumber << std::endl;
  }

  return 0;
}

Now notice that the first and third numbers are identical (as you'd expect) and the results are consistent between runs:

% ./a.out
2244390274
3654371666
2244390274
% ./a.out
2244390274
3654371666
2244390274
% ./a.out
2244390274
3654371666
2244390274