2

I am trying to make a mini lottery simulator. I want to generate 8 random numbers with a minimum of 1 and a maximum of 20. And i store these generated numbers in a Lottery object. I have a LotteryManager class, where i can decide how many times do i want to generate these lotteries. But i always get the same random numbers. I seeded the generator so I dont understand whats the problem with it. Any help is appreciated!

This is my lottery class:

class Lottery
{
private:
    vector<int> lotteryA;
    int lotteryB;
public:
    Lottery();

    vector<int> getLotteryA() const;
    int getLotteryB() const;

    void generateLotteryA();
    void generateLotteryB();
    void printLottery() const;
};

And this is the class that manages it:

class LotteryManager
{
private:
    unsigned int quanity = 0;
    map<unsigned int, Lottery> lotteries;
public:
    LotteryManager();
    LotteryManager(unsigned int q);

    unsigned int getQuanity() const;
    void setQuanity(unsigned int value);

    void generateLotteries();
    void printLotteries() const;
};

And this is my main code:

LotteryManager* lm = new LotteryManager(100);
    lm->generateLotteries();
    lm->printLotteries();

The generate and print functions in the LotteryManager class:

void LotteryManager::generateLotteries()
{
    for(unsigned int i = 0; i < getQuanity(); ++i)
    {
        Lottery l;
        l.generateLotteryA();
        l.generateLotteryB();
        lotteries.insert(make_pair(i, l));
        l.printLottery();
    }
}

void LotteryManager::printLotteries() const
{
    cout << "Lotteries:" << endl;
    for(auto current : lotteries)
    {
        cout << current.first << ".: ";
        current.second.printLottery();
    }
}

And the generate functions in the Lottery class: (The generate A and B are just the different fields, because the game aims to imitate the game named Putto, where you need to guess 8 numbers from 20 in the field A and 1 from 4 in the field B)

void Lottery::generateLotteryA()
{
    random_device rn_d;
    mt19937 rng(rn_d());
    uniform_int_distribution<int> dist(1, 20);
    for(unsigned int i = 0; i < 8; ++i)
    {
        lotteryA.push_back(dist(rng));
    }
}

void Lottery::generateLotteryB()
{
    srand(time(0));
    lotteryB = (rand() % 20) + 1;
}

Thanks in advance!

  • *"...when i properly seeded my generator?"* You did not properly seed the generator, because you keep re-seeding it. Try only seeding it once, and have it be a singleton. – Eljay Nov 17 '20 at 13:27
  • @yazan in principle the problem is the same, but I wouldn't make questions about `` duplicate of a question about `rand()`, thats a bit backwards – 463035818_is_not_an_ai Nov 17 '20 at 13:44
  • @idclev463035818 Definitely. On the other hand, this *must* be a dupe right? I looked, and found plenty of `rand` targets, but nothing for this :p – cigien Nov 17 '20 at 13:46
  • Does this answer your question? [srand() — why call it only once?](https://stackoverflow.com/questions/7343833/srand-why-call-it-only-once) – Peter O. Nov 17 '20 at 18:45

1 Answers1

5

In Lottery::generateLotteryA you are declaring a random engine, and random device. Every time you call this function, the engine is seeded with the same random device, and so you could get the same results.


It might seem reasonable that invoking rn_d() each time with different random_devices will generate a different value. However, this is not required, and as mentioned in the reference:

... each std::random_device object may generate the same number sequence.

In practice, most implementations will generate different values, and so this is not really a problem. You could even consider it a defect in the standard library for not making this guarantee. However, as currently specified, you could see the same values being produced.


The simplest fix would be to make the engine static, so it only gets seeded the first time the function is called:

void Lottery::generateLotteryA()
{
    // ...
    static mt19937 rng(rn_d());
    // ...
}
cigien
  • 57,834
  • 11
  • 73
  • 112
  • The code shown by the OP does not generate random numbers that would match the requirements of `mt19937`, but shouldn’t `random_device rn_d; rn_d();` still return a random number (except it the implementation of `random_device` is broken)? – t.niese Nov 17 '20 at 13:46
  • @t.niese Good question. For reasons unclear to me, the results of calling this device is implementation defined, and it is allowed to produce the same sequence every time. See the second sentence [here](https://en.cppreference.com/w/cpp/numeric/random/random_device). – cigien Nov 17 '20 at 13:48
  • I saw your edit just at the moment i send the comment. – t.niese Nov 17 '20 at 13:54
  • @t.niese Ah, ok, so the edit makes it clear enough now? – cigien Nov 17 '20 at 13:55
  • The only implementation with a bad `random_device` (that I know) was MinGW. It's fixed starting from GCC 9.2. – HolyBlackCat Nov 17 '20 at 13:57
  • Yes it does. But I still would consider it as a bug, and most likely the OP uses MinGW. Because now the OP will most likely wonder why the random numbers won't cange for `generateLotteryA` on each run of the application. – t.niese Nov 17 '20 at 13:57
  • @HolyBlackCat Maybe so, but I'm not aware of the standard making that guarantee, so the OP's code is still not guaranteed to produce different values, right? – cigien Nov 17 '20 at 13:58
  • @cigien Strictly speaking it's not guaranteed, yep, but I'd expect it to do the right thing on any sane implementation. (I'm not trying to argue with the answer.) – HolyBlackCat Nov 17 '20 at 14:00
  • @t.niese Maybe so, but the answer explains why that could happen. – cigien Nov 17 '20 at 14:01
  • @HolyBlackCat No worries, I'd like the answer to be good :) I've added in the points you've made to the answer. Do you think that's better? – cigien Nov 17 '20 at 14:04
  • Whoa! Thanks for the explanation and solution, it works wonderfully. I have one more question tho. Is there any way to get more sophisticated random numbers than these? I know that you cant get truly random numbers, but would there be a "more random" way to do this? Or this is just what i can make of if? Thanks in advance! – Adam Bencsik Nov 24 '20 at 07:11