0

I need my program to simulate bernoulli (p) random variables with ability to change p. If i write smth like this

#include <random>
#include <iostream>
#include <chrono>
int main()
{
double prob = 0.1; //let it be const for now, no need to change it in this example
std::mt19937 gen(std::chrono::steady_clock::now().time_since_epoch().count());
std::bernoulli_distribution coin(prob);

int success = 0;
for (int i = 0; i < 1000; ++i) {
  if (coin(prob)) ++success;
}
  std::cout << success << "\n";
    
  return 0;
}

it works perfectly fine. But i need to change parameter p, so i decided to create a function which takes some switcher, simulates random variable with parameter depending on switcher, and returns true or false. Here is my code:

#include <random>
#include <iostream>
#include <chrono>

 bool coin_flip(int switcher) {
   double prob;
      switch (switcher) {
      case 1: {
    prob = 0.1;
    std::mt19937 gen(std::chrono::steady_clock::now().time_since_epoch().count());
    std::bernoulli_distribution coin(prob);
    return coin(gen);
      }
      case 2: {
    prob = 0.2;
    std::mt19937 gen(std::chrono::steady_clock::now().time_since_epoch().count());
    std::bernoulli_distribution coin(prob);
    return coin(gen);
      }

      }
    }
int main()
{
    int success = 0;
    
    for (int i = 0; i < 1000; ++i) {
      if (coin_flip(1)) {
    ++success;
      }
    }

    std::cout << success << "\n";
    
    return 0;
}

The for loop in the main body counts number of successes among 1000 tries and i expect it to be around 100, but i do get wierd results. Too many zeroes and large numbers.

So my question is why does coin(prob) breaks when passes to the output of a function?

Also, if you have good ideas, i would be grateful for any suggestions on how to simulate sequence of bernoulli r.v's with different parameters p (e.g. we are in a loop where p depends on counter i)

Thank you for your time

Ilya68
  • 3
  • 1

2 Answers2

1

You should not create a new random number generator for every roll, that breaks the whole pseudo-randomness.

Create a global(-ish) generator and reuse it in every function call:

bool coin_flip(int switcher) {
    static std::mt19937 gen(std::chrono::steady_clock::now().time_since_epoch().count());

    switch(switcher)
    {
        case 1:
            std::bernoulli_distribution coin(0.1);
            return coin(gen);
        case 2:
            std::bernoulli_distribution coin(0.2);
            return coin(gen);
    }
    throw std::invalid_argument{"Incorrect switcher value"};
}

static local variables are only initialized once, during first function call, and remain accessible in every other function call. They are global in terms of lifetime (they will only die when the program finishes), but only accessible from within this function.

Yksisarvinen
  • 18,008
  • 2
  • 24
  • 52
  • Thx for the answer. but what if i need hundred and thousands of generators? if i create them all ahead in the loop. they will take lots of memory.. Also, my task assumes that i will create r.v's depending on the results of previous simulations. I could create all the possible generators ahead, but it looks like overkilling and i dont like it at all. – Ilya68 Dec 03 '20 at 12:08
  • Thx again, I’m not familiar with maps, need some time to dig into it. Will watch it a bit later – Ilya68 Dec 03 '20 at 12:47
  • 1
    @Ilya68 Hmm, I think I might have gone too far with this. If I'm not mistaken, the only global element you need is the generator, distributions are fine to be recreated every time (distributions only adjust the generator output). I need to think about it for a while, I'll try to update the answer later. – Yksisarvinen Dec 03 '20 at 13:03
  • 1
    @Yksisarvinen agree with the previous comment. I've always created distributions as I've needed them, and haven't seen anything in the docs suggesting otherwise. The RNG certainly needs to be kept around, but that's it AFAIK – Sam Mason Dec 03 '20 at 15:28
1

You could create a Coin class holding a particular distribution with a given probability and then store several instances of that in a container

#include <random>
#include <iostream>
#include <vector>

template< class Dist >
class Coin
{
    Dist dist_;

  public:
    Coin(double p) : dist_{p} {}

    template< class Gen >
    bool toss(Gen& gen) { return dist_(gen); }
};

int main()
{
    std::seed_seq ss{ 42, 987654321, 17, 65535 };
    std::mt19937 gen{ ss };

    using coin_t = Coin<std::bernoulli_distribution>;

    std::vector<coin_t> coins{
        0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9
    };

    std::vector<int> successes(coins.size());
    
    for (size_t i = 0; i < 1000; ++i) {
        for (size_t c = 0; c < coins.size(); ++c) {
            successes[c] += coins[c].toss(gen);
        }
    }

    for (auto s : successes) {
        std::cout << s << '\n';
    }
}

Testable here.

Bob__
  • 12,361
  • 3
  • 28
  • 42