1

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
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    You aren't resetting `randomnumber ` back to zero before each test. – François Andrieux Jul 22 '23 at 18:32
  • 5
    Could you show us code where you "tried setting the seed"? This code is using `std::random_device`, which is literally described as _"non-deterministic random number generator using hardware entropy source"_ – Brian61354270 Jul 22 '23 at 18:33
  • 2
    @FrançoisAndrieux It's used in `dist(rd)`. – tadman Jul 22 '23 at 18:34
  • 2
    You're initializing an `mt` in a void context, basically throwing it in the trash immediately. Why? Did you mean to use that in place of `rd`? – tadman Jul 22 '23 at 18:35
  • 1
    This might be a good time to learn how to use a [*debugger*](https://stackoverflow.com/questions/25385173/what-is-a-debugger-and-how-can-it-help-me-diagnose-problems) to step through your code line by line while monitoring variables and their values. – Some programmer dude Jul 22 '23 at 18:35
  • I missed the fact that you're creating some unused `std::mt19937`s before my first comment. Is that what you meant by tried setting the seed? You need to use those as your generator (i.e. instead of `std::random_device`). – Brian61354270 Jul 22 '23 at 18:37
  • 1
    @tadman You're right, I was distracted by the unnamed `std::mt19937` assuming that it was the generator being used. – François Andrieux Jul 22 '23 at 18:38

4 Answers4

2

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
Adam
  • 16,808
  • 7
  • 52
  • 98
0

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;

Severin Pappadeux
  • 18,636
  • 3
  • 38
  • 64
0

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.

M.M
  • 138,810
  • 21
  • 208
  • 365
-1

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

  • 2
    `srand()` and `rand()` are poor PRNG. Perhaps they are adequate for your needs. – Eljay Jul 22 '23 at 19:39
  • 4
    Switching to `rand()` is a downgrade, and more importantly sidesteps learning from your mistakes, which is an essential part of improving. – François Andrieux Jul 22 '23 at 19:51
  • 1
    note that using `srand` and `rand` might appear repeatable, but the output will likely change on a different system. e.g. try comparing on Windows, Linux and MacOS and I'd expect `rand` to give different numbers for the same seed. more specifically it's unspecified what algorithm is used, so even upgrading something apparently unrelated might cause it to give different numbers back – Sam Mason Jul 22 '23 at 22:42
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Mohamed Fathallah Jul 28 '23 at 20:18