【发布时间】:2016-06-08 06:08:40
【问题描述】:
我有 N 个数字 n_1, n_2, ...n_N 和相关的概率 p_1, p_2, ..., p_N。
函数应返回数字 n_i,概率为 p_i,其中 i = 1, ..., N。
如何在 C++ 中建模?
我知道这不是一个难题。但我是 C++ 新手,想知道你会使用什么函数。
你会像这样生成 0 到 1 之间的统一随机数吗:
((double) rand() / (RAND_MAX+1))
【问题讨论】:
我有 N 个数字 n_1, n_2, ...n_N 和相关的概率 p_1, p_2, ..., p_N。
函数应返回数字 n_i,概率为 p_i,其中 i = 1, ..., N。
如何在 C++ 中建模?
我知道这不是一个难题。但我是 C++ 新手,想知道你会使用什么函数。
你会像这样生成 0 到 1 之间的统一随机数吗:
((double) rand() / (RAND_MAX+1))
【问题讨论】:
这与我为这个问题给出的答案非常相似:
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。
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>,您可以使用第一个来源下方稍作修改的版本。
#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);
}
};
<random> 版本#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
【讨论】:
可能类似于(未经测试的代码!)
/* 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];
}
}
【讨论】:
你在我上一条评论中有一个正确的答案:
how-to-select-a-value-from-a-list-with-non-uniform-probabilities
【讨论】: