C++ Problem when trying to set bernoulli distribution as an output for function

632 views Asked by At

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

2

There are 2 answers

4
Yksisarvinen On BEST ANSWER

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.

0
Bob__ On

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.