c ++,给定概率随机选择数字

时间:2011-12-27 07:28:03

标签: c++ math

我有N个数n_1,n_2,... n_N和相关概率p_1,p_2,...,p_N。 函数应以概率p_i返回数字n_i,其中i = 1,...,N。 如何在c ++中建模?
我知道这不是一个难题。但我是c ++的新手,想知道你将使用什么功能。 你会产生0到1之间的统一随机数,如下所示:

((double) rand() / (RAND_MAX+1))

4 个答案:

答案 0 :(得分:3)

这与我为这个问题给出的答案非常相似:

changing probability of getting a random number

你可以这样做:

double val = (double)rand() / RAND_MAX;

int random;
if (val < p_1)
    random = n_1;
else if (val < p_1 + p_2)
    random = n_2;
else if (val < p_1 + p_2 + p_3)
    random = n_3;
else
    random = n_4;

当然,这种方法只有p_1 + p_2 + p_3 + p_4 == 1.0

才有意义

这很容易推广到具有几个数组和简单循环的可变数量的输出和概率。<​​/ p>

答案 1 :(得分:1)

如果你知道编译时的概率,你可以使用我决定创建的这个可变参数模板版本。虽然实际上我并不建议使用它,因为源头是多么令人难以理解:P。

用法

NumChooser <
    Entry<2, 10>, // Value of 2 and relative probability of 10
    Entry<5, 50>,
    Entry<6, 80>,
    Entry<20, 01>
> chooser;

chooser.choose(); // Returns the number 2 on average 10/141 times, etc.

效率

Ideone 通常,基于模板的实现与基本实现非常相似。但是,存在一些差异:

  • 使用-O2优化或不进行优化,模板版本可以慢〜1-5%
  • 通过-O3优化,模板版本在连续生成1到10,000次数字时实际上快了约1%。

注释

这使用rand()来选择数字。如果统计上准确对您很重要,或者您想使用C ++ 11 <random>,则可以使用第一个来源下方略微修改的版本。

来源

Ideone

#define onlyAtEnd(a) typename std::enable_if<sizeof...(a) == 0 > ::type

template<int a, int b>
class Entry
{
public:
    static constexpr int VAL = a;
    static constexpr int PROB = b;
};

template<typename... EntryTypes>
class NumChooser
{
private:
    const int SUM;
    static constexpr int NUM_VALS = sizeof...(EntryTypes);

public:
    static constexpr int size()
    {
        return NUM_VALS;
    }

    template<typename T, typename... args>
    constexpr int calcSum()
    {
        return T::PROB + calcSum < args...>();
    }

    template <typename... Ts, typename = onlyAtEnd(Ts) >
    constexpr int calcSum()
    {
        return 0;
    }

    NumChooser() : SUM(calcSum < EntryTypes... >()) { }

    template<typename T, typename... args>
    constexpr int find(int left, int previous = 0)
    {
        return left < 0 ? previous : find < args... >(left - T::PROB, T::VAL);
    }

    template <typename... Ts, typename = onlyAtEnd(Ts) >
    constexpr int find(int left, int previous)
    {
        return previous;
    }

    constexpr int choose()
    {
        return find < EntryTypes... >(rand() % SUM);
    }
};

C ++ 11 <random>

Ideone

#include <random>
#define onlyAtEnd(a) typename std::enable_if<sizeof...(a) == 0 > ::type

template<int a, int b>
class Entry
{
public:
    static constexpr int VAL = a;
    static constexpr int PROB = b;
};

template<typename... EntryTypes>
class NumChooser
{
private:
    const int SUM;
    static constexpr int NUM_VALS = sizeof...(EntryTypes);
    std::mt19937 gen;
    std::uniform_int_distribution<> dist;

public:

    static constexpr int size()
    {
        return NUM_VALS;
    }

    template<typename T, typename... args>
    constexpr int calcSum()
    {
        return T::PROB + calcSum < args...>();
    }

    template <typename... Ts, typename = onlyAtEnd(Ts) >
    constexpr int calcSum()
    {
        return 0;
    }

    NumChooser() : SUM(calcSum < EntryTypes... >()), gen(std::random_device{}()), dist(1, SUM) { }

    template<typename T, typename... args>
    constexpr int find(int left, int previous = 0)
    {
        return left < 0 ? previous : find < args... >(left - T::PROB, T::VAL);
    }

    template <typename... Ts, typename = onlyAtEnd(Ts) >
    constexpr int find(int left, int previous)
    {
        return previous;
    }

    int choose()
    {
        return find < EntryTypes... >(dist(gen));
    }
};

// Same usage as example above

答案 2 :(得分:0)

也许像(未经测试的代码!)

/* n is the size of tables, numtab[i] the number of index i, 
   probtab[i] its probability; the sum of all probtab should be 1.0 */
int random_inside(int n, int numtab[], double probtab[])
{
  double r = drand48();
  double p = 0.0;
  for (int i=0; i<n; i++) {
    p += probtab[i];
    if (r>=p) return numtab[i];
  }
}

答案 3 :(得分:0)

在上一条评论中,您有正确答案:

how-to-select-a-value-from-a-list-with-non-uniform-probabilities