1

I wonder how one would go about coding weighted random in C++—having a random outcome but where you can control the outcome percentage of each result. I have searched the web but have yet to find anything but the Javascript version.

I want to program a question game, which I have programmed in python previously without being able to controll the percentages. Still, I want to transfer the program to C++ as there is more controllability. The program would have a list of questions; currently, the python version has just multiplication table questions, but I would like to have other genres of questions once I get this weighted random figured out. So far, I have been able to get the python code to stop asking a question that was correctly answered. Still, I would like it to slightly lessen the chance of it appearing as a question instead of it just not appearing after correctly answered once. If someone can explain how to get a weighted random in C++, I can transfer the rest of the code to C++ without issue.

2 Answers2

3

Have a look at std::discrete_distribution:

#include <iostream>
#include <iomanip>
#include <map>
#include <random>
 
int main()
{
    std::random_device rd;
    std::mt19937 gen(rd());
    std::discrete_distribution<> d({40, 10, 10, 40});
    std::map<int, int> map;
    for(int n=0; n<10000; ++n) {
        ++map[d(gen)];
    }
    for(const auto& [num, count] : map) {
        std::cout << num << " generated " << std::setw(4) << count << " times\n";
    }
}
davidhigh
  • 14,652
  • 2
  • 44
  • 75
1

Besides correct and simple other answer I can suggest my own version which explicitly does all math computation from scratch. For educational purpose only.

Try it online!

#include <cstdint>
#include <vector>
#include <random>
#include <iostream>
#include <iomanip>

int main() {
    std::vector<double> const weights = {9.0, 7.8, 1.2, 3.4, 5.6};
    std::vector<double> sum = {0};
    for (auto w: weights)
        sum.push_back(sum.back() + w);
    for (auto & s: sum)
        s /= sum.back();
    std::mt19937_64 rng{std::random_device{}()};
    auto Gen = [&]{
        auto x = double(rng()) / double(uint64_t(-1));
        for (size_t i = 0; i < sum.size() - 1; ++i)
            if (x < sum[i + 1])
                return i;
        return sum.size() - 2;
    };
    std::vector<size_t> counts(weights.size());
    for (size_t i = 0; i < (1 << 12); ++i)
        ++counts[Gen()];
    for (size_t i = 0; i < counts.size(); ++i)
        std::cout << std::setw(2) << i << " was generated "
            << std::setw(4) << counts[i] << " times" << std::endl;
}

Console output:

 0 was generated 1404 times
 1 was generated 1165 times
 2 was generated  166 times
 3 was generated  524 times
 4 was generated  837 times
Arty
  • 14,883
  • 6
  • 36
  • 69