需要一个正态分布的随机数生成器
Need a normally-distributed random number generator
我是 C++ 新手,但有数学背景。我正在尝试创建一个随机数生成器,它将吐出一个介于 1.00 和 2.00 之间的小数(美元金额),具有近似正态分布(因此平均值为 1.50)。
现在,我从来没有尝试过这样的事情,也没有找到类似的问题,专门从一组具有正态分布概率的数字中吐出一个元素。在这个模型中,1.50最有可能出现,1.00或2.00几乎没有机会出现。
写一个 p.d.f 很容易。对于 mean=1.50 和 3*sigma = 0.5 --> sigma = 1/6 的正态分布(因此几乎所有数据都在 1.00 和 2.00 之间)。然而,不仅仅是不知道如何用 C++ 集成这条曲线下的 101 个区域(我认为这不能通过分析解决),它对我来说听起来效率不高。我知道C++有正态分布函数
有人可以帮我写出来,并附上评论吗?谢谢
C++ 标准库有一个 normal distribution class - 正是您所要求的。使用它 - 并裁剪该值,使其介于最小值和最大值之间:
#include <algorithm> // for std::clamp()
#include <iostream>
#include <random>
int main() {
std::random_device randomness_device{};
std::mt19937 pseudorandom_generator{randomness_device()};
auto mean = 1.5;
auto std_dev = 0.5;
auto min_allowed = 1.0;
auto max_allowed = 2.0;
std::normal_distribution<> distribution{mean, std_dev};
auto sample = distribution(pseudorandom_generator);
auto clamped =
// C++17 and later
std::clamp(sample, min_allowed, max_allowed);
// C++14 or earlier:
// std::max(min_allowed,std::min(sample, max_allowed));
//
std::cout
<< "A value from a normal distribution with mean " << mean
<< " and standard deviation " << std_dev << ": " << sample
<< "; when clamped to [" << min_allowed << ", "
<< max_allowed << "], we get: " << clamped << "\n";
}
就分布而言 - 这改变了度量,使整个范围 (-infinity,1) 集中在 1,同样 (2,infinity) 集中在 2。作为评论者,建议,有其他解释您对 "approximately normal" 分布的请求的方法,例如重新采样,直到您达到所需范围内的值;或应用将 (infinity, infinity) 映射到 (1,2) 的连续变换,例如x -> arctan(x)。但是你没有具体说明你到底在追求什么。
您可以为此使用 std::normal_distribution<>
。
它从 double
数据类型的整个范围内生成样本,但您可以丢弃您感兴趣的区间之外的样本,您仍然会接近具有三西格玛的正态分布。
#include <iostream>
#include <random>
using namespace std;
int main()
{
auto mean = 1.5,
stddev = 1.0 / 6;
// Create a normal distribution to pull samples from
// The distribution has mean 1.5 and ~1/6 std dev
random_device rd;
mt19937_64 generator(rd());
normal_distribution<> distribution(mean, stddev);
cout << "some samples:" << endl;
for (int i = 0; i < 10; ++i)
{
// Generate a sample. The sample will be in (-infinity, infinity), so
// we throw away values that are outside of 3 std devs.
// The distribution will no longer be normal, but close enough.
double v;
do
{
v = distribution(generator);
} while (v < mean - 3 * stddev || v >= mean + 3 * stddev);
cout << v << endl;
}
return 0;
}
some samples:
1.70539
1.49569
1.53731
1.42872
1.34029
1.54886
1.66154
1.54685
1.60833
1.36282
添加到其他答案中,您可以使用(伪)unform 随机数生成器通过使用 Polar method
来制作普通随机数生成器
示例代码:
#include <math.h>
#include <stdlib.h>
double
randn (double mu, double sigma)
{
double U1, U2, W, mult;
static double X1, X2;
static int call = 0;
if (call == 1)
{
call = !call;
return (mu + sigma * (double) X2);
}
do
{
U1 = -1 + ((double) rand () / RAND_MAX) * 2;
U2 = -1 + ((double) rand () / RAND_MAX) * 2;
W = pow (U1, 2) + pow (U2, 2);
}
while (W >= 1 || W == 0);
mult = sqrt ((-2 * log (W)) / W);
X1 = U1 * mult;
X2 = U2 * mult;
call = !call;
return (mu + sigma * (double) X1);
}
接下来,您可以使用它来为您的应用程序获取正态分布的随机数。
我是 C++ 新手,但有数学背景。我正在尝试创建一个随机数生成器,它将吐出一个介于 1.00 和 2.00 之间的小数(美元金额),具有近似正态分布(因此平均值为 1.50)。
现在,我从来没有尝试过这样的事情,也没有找到类似的问题,专门从一组具有正态分布概率的数字中吐出一个元素。在这个模型中,1.50最有可能出现,1.00或2.00几乎没有机会出现。
写一个 p.d.f 很容易。对于 mean=1.50 和 3*sigma = 0.5 --> sigma = 1/6 的正态分布(因此几乎所有数据都在 1.00 和 2.00 之间)。然而,不仅仅是不知道如何用 C++ 集成这条曲线下的 101 个区域(我认为这不能通过分析解决),它对我来说听起来效率不高。我知道C++有正态分布函数
有人可以帮我写出来,并附上评论吗?谢谢
C++ 标准库有一个 normal distribution class - 正是您所要求的。使用它 - 并裁剪该值,使其介于最小值和最大值之间:
#include <algorithm> // for std::clamp()
#include <iostream>
#include <random>
int main() {
std::random_device randomness_device{};
std::mt19937 pseudorandom_generator{randomness_device()};
auto mean = 1.5;
auto std_dev = 0.5;
auto min_allowed = 1.0;
auto max_allowed = 2.0;
std::normal_distribution<> distribution{mean, std_dev};
auto sample = distribution(pseudorandom_generator);
auto clamped =
// C++17 and later
std::clamp(sample, min_allowed, max_allowed);
// C++14 or earlier:
// std::max(min_allowed,std::min(sample, max_allowed));
//
std::cout
<< "A value from a normal distribution with mean " << mean
<< " and standard deviation " << std_dev << ": " << sample
<< "; when clamped to [" << min_allowed << ", "
<< max_allowed << "], we get: " << clamped << "\n";
}
就分布而言 - 这改变了度量,使整个范围 (-infinity,1) 集中在 1,同样 (2,infinity) 集中在 2。作为评论者,建议,有其他解释您对 "approximately normal" 分布的请求的方法,例如重新采样,直到您达到所需范围内的值;或应用将 (infinity, infinity) 映射到 (1,2) 的连续变换,例如x -> arctan(x)。但是你没有具体说明你到底在追求什么。
您可以为此使用 std::normal_distribution<>
。
它从 double
数据类型的整个范围内生成样本,但您可以丢弃您感兴趣的区间之外的样本,您仍然会接近具有三西格玛的正态分布。
#include <iostream>
#include <random>
using namespace std;
int main()
{
auto mean = 1.5,
stddev = 1.0 / 6;
// Create a normal distribution to pull samples from
// The distribution has mean 1.5 and ~1/6 std dev
random_device rd;
mt19937_64 generator(rd());
normal_distribution<> distribution(mean, stddev);
cout << "some samples:" << endl;
for (int i = 0; i < 10; ++i)
{
// Generate a sample. The sample will be in (-infinity, infinity), so
// we throw away values that are outside of 3 std devs.
// The distribution will no longer be normal, but close enough.
double v;
do
{
v = distribution(generator);
} while (v < mean - 3 * stddev || v >= mean + 3 * stddev);
cout << v << endl;
}
return 0;
}
some samples:
1.70539
1.49569
1.53731
1.42872
1.34029
1.54886
1.66154
1.54685
1.60833
1.36282
添加到其他答案中,您可以使用(伪)unform 随机数生成器通过使用 Polar method
来制作普通随机数生成器示例代码:
#include <math.h>
#include <stdlib.h>
double
randn (double mu, double sigma)
{
double U1, U2, W, mult;
static double X1, X2;
static int call = 0;
if (call == 1)
{
call = !call;
return (mu + sigma * (double) X2);
}
do
{
U1 = -1 + ((double) rand () / RAND_MAX) * 2;
U2 = -1 + ((double) rand () / RAND_MAX) * 2;
W = pow (U1, 2) + pow (U2, 2);
}
while (W >= 1 || W == 0);
mult = sqrt ((-2 * log (W)) / W);
X1 = U1 * mult;
X2 = U2 * mult;
call = !call;
return (mu + sigma * (double) X1);
}
接下来,您可以使用它来为您的应用程序获取正态分布的随机数。