如何在 C++ 中生成具有均值的随机数?
How do I generate random numbers with a mean in C++?
如何在 C++ 中生成 1,000 到 20,000 之间的 100 个随机数,总平均值为 9,000?我正在研究 C++ 11 库,但没有找到允许我包含平均值和范围的方法。
由于您不关心分布,只要它满足您的约束,到目前为止,最简单的做法就是始终生成 9000
。最简单的分布不是像 1000
的概率 p
和 20000
的概率 1-p
,你已经解决了 [=12] 的值=] 给出正确的平均值。
我强烈怀疑你应该在开始考虑编程之前弄清楚你想做什么math/statistics。
以下是如何使用正态分布执行此操作,使用样本丢弃技术将值保持在范围内。这当然会扭曲分布,因此它不再正常。这种影响有多大取决于您最初选择的标准差。这对您有多重要取决于您的应用程序。
#include <iostream>
#include <random>
double get_random_number_with_minimum_mean_and_maximum(
double minimum,
double mean,
double maximum
) {
// Any uniform random generator of your choice could be used here.
// Obviously making this static is not rentrant or thread-safe.
static std::mt19937 generator;
// We'll start with a normal distribution with a standard deviation set
// such that ~99.7% of the time we'll get a number within the average width
// of your upper and lower bounds.
//
// Why ~99.7% and not some other number? Because that corresponds to three
// standard deviations, and you didn't specify any requirements, so I'm
// just making assumptions on your behalf.
double const average_bound_width = ((mean-minimum) + (maximum-mean)) / 2;
double const standard_deviation = average_bound_width / 3;
std::normal_distribution<double> distribution(mean, standard_deviation);
// Now, keep fetching numbers until we find one that is within our desired
// bounds. Throwing numbers away randomly from a normal distribution does
// not affect the mean, but since our bounds are going to be skewed, our
// mean will probably get skewed slightly, but this will likely not be
// very noticable.
double value;
do {
value = distribution(generator);
} while (value < minimum || maximum < value);
return value;
}
int main() {
for (int i=0; i<100; ++i) {
std::cout << get_random_number_with_minimum_mean_and_maximum(
1000,
9000,
20000
) << '\n';
}
}
当我运行这个时,我得到这个输出:
9426.01
10458.7
9518.42
9945.55
9032.35
7268.34
11092.2
13705.6
6374.58
7290.7
7008.3
10075.4
15678
8089.93
5645.39
13607.6
11930.2
13799.4
12194.7
10390
8594.2
14625.4
10487.9
11116.9
9473.06
13868
12414.5
12711.2
9431.92
3570.35
3490.47
3974.69
6695.1
8642.8
10034.3
8757.84
10232.6
10441.1
18234.9
9862.67
2365.8
9982.83
10282
13492.3
11932.6
9399.23
7196.37
11793.8
9646.56
1232.16
7796.91
13297.7
13191.4
8340.9
9891.94
7998.53
8139.9
8813.12
8829.3
9408.99
7771.22
6957.75
6149.01
7139.31
1482.41
5893.11
12720.9
6009.68
12360.5
5557.36
3080.25
8922.16
7636.47
12109.1
11153.5
5434.98
8874.9
8599.4
7833.87
8525.87
7630.14
8595.15
9786.19
12644.8
6310.17
12696.1
8717.86
7199.22
7404.67
7410.03
6041.42
7930.46
6505.42
7772.88
4929.65
4686.06
7743.93
5211.43
12023.2
10380.5
平均值约为 8980,对于仅 100 个样本而言非常接近 9000。
如果我 运行 这有 100 万个样本,平均值是 ~9049。
由于您在分配上很灵活,因此无需执行拒绝逻辑即可提供合理结果的简单解决方案是 triangular distribution。 IE。您将三角形的下端设置为 1,000,将三角形的上端设置为 20,000,然后将三角形的尖端设置为您想要的平均值 9,000。
上面的维基百科link表示三角分布的均值是:
(a + b + c) / 3
其中 a
和 b
分别是您的下限和上限,c
是您的三角形的尖端。对于您的输入,简单的代数表明 c = 6,000
将给出您想要的平均值 9,000。
C++ 的 <random>
头文件中有一个名为 std::piecewise_linear_distribution
的分布,它非常适合设置三角分布。这只需要两条直线。构建这种三角分布的一种简单方法是:
std::piecewise_linear_distribution<> dist({1000., 6000., 20000.},
[](double x)
{
return x == 6000 ? 1. : 0.;
});
现在您只需将 URNG 插入此发行版并生成结果。为了理智起见,根据您的问题陈述收集一些重要的统计数据也很有帮助,例如最小值、最大值和平均值。
这是一个完整的程序:
#include <algorithm>
#include <iostream>
#include <numeric>
#include <random>
#include <vector>
int
main()
{
std::mt19937_64 eng;
std::piecewise_linear_distribution<> dist({1000., 6000., 20000.},
[](double x)
{
return x == 6000 ? 1. : 0.;
});
std::vector<double> results;
for (int i = 0; i < 100; ++i)
results.push_back(dist(eng));
auto avg = std::accumulate(results.begin(), results.end(), 0.) / results.size();
auto minmax = std::minmax_element(results.begin(), results.end());
std::cout << "size = " << results.size() << '\n';
std::cout << "min = " << *minmax.first << '\n';
std::cout << "avg = " << avg << '\n';
std::cout << "max = " << *minmax.second << '\n';
}
这个应该可移植输出:
size = 100
min = 2353.05
avg = 8972.1
max = 18162.5
如果将采样值的数量增加到足够高,您将看到参数收敛:
size = 10000000
min = 1003.08
avg = 8998.91
max = 19995.5
根据需要播种。
这里有一个是连续的,可以return范围内的任意值,一次性执行。均值符合要求,但众数通常在两端最接近均值。
double shiftable_distribution(double minimum, double mean, double maximum)
{
double range = maximum - minimum;
double scaledmean = (mean - minimum) / range;
return pow((double)rand() / RAND_MAX,
(1.0 - scaledmean) / scaledmean)
* range + minimum;
}
实际分布情况(对于 1000、9000、20000)
如何在 C++ 中生成 1,000 到 20,000 之间的 100 个随机数,总平均值为 9,000?我正在研究 C++ 11 库,但没有找到允许我包含平均值和范围的方法。
由于您不关心分布,只要它满足您的约束,到目前为止,最简单的做法就是始终生成 9000
。最简单的分布不是像 1000
的概率 p
和 20000
的概率 1-p
,你已经解决了 [=12] 的值=] 给出正确的平均值。
我强烈怀疑你应该在开始考虑编程之前弄清楚你想做什么math/statistics。
以下是如何使用正态分布执行此操作,使用样本丢弃技术将值保持在范围内。这当然会扭曲分布,因此它不再正常。这种影响有多大取决于您最初选择的标准差。这对您有多重要取决于您的应用程序。
#include <iostream>
#include <random>
double get_random_number_with_minimum_mean_and_maximum(
double minimum,
double mean,
double maximum
) {
// Any uniform random generator of your choice could be used here.
// Obviously making this static is not rentrant or thread-safe.
static std::mt19937 generator;
// We'll start with a normal distribution with a standard deviation set
// such that ~99.7% of the time we'll get a number within the average width
// of your upper and lower bounds.
//
// Why ~99.7% and not some other number? Because that corresponds to three
// standard deviations, and you didn't specify any requirements, so I'm
// just making assumptions on your behalf.
double const average_bound_width = ((mean-minimum) + (maximum-mean)) / 2;
double const standard_deviation = average_bound_width / 3;
std::normal_distribution<double> distribution(mean, standard_deviation);
// Now, keep fetching numbers until we find one that is within our desired
// bounds. Throwing numbers away randomly from a normal distribution does
// not affect the mean, but since our bounds are going to be skewed, our
// mean will probably get skewed slightly, but this will likely not be
// very noticable.
double value;
do {
value = distribution(generator);
} while (value < minimum || maximum < value);
return value;
}
int main() {
for (int i=0; i<100; ++i) {
std::cout << get_random_number_with_minimum_mean_and_maximum(
1000,
9000,
20000
) << '\n';
}
}
当我运行这个时,我得到这个输出:
9426.01
10458.7
9518.42
9945.55
9032.35
7268.34
11092.2
13705.6
6374.58
7290.7
7008.3
10075.4
15678
8089.93
5645.39
13607.6
11930.2
13799.4
12194.7
10390
8594.2
14625.4
10487.9
11116.9
9473.06
13868
12414.5
12711.2
9431.92
3570.35
3490.47
3974.69
6695.1
8642.8
10034.3
8757.84
10232.6
10441.1
18234.9
9862.67
2365.8
9982.83
10282
13492.3
11932.6
9399.23
7196.37
11793.8
9646.56
1232.16
7796.91
13297.7
13191.4
8340.9
9891.94
7998.53
8139.9
8813.12
8829.3
9408.99
7771.22
6957.75
6149.01
7139.31
1482.41
5893.11
12720.9
6009.68
12360.5
5557.36
3080.25
8922.16
7636.47
12109.1
11153.5
5434.98
8874.9
8599.4
7833.87
8525.87
7630.14
8595.15
9786.19
12644.8
6310.17
12696.1
8717.86
7199.22
7404.67
7410.03
6041.42
7930.46
6505.42
7772.88
4929.65
4686.06
7743.93
5211.43
12023.2
10380.5
平均值约为 8980,对于仅 100 个样本而言非常接近 9000。
如果我 运行 这有 100 万个样本,平均值是 ~9049。
由于您在分配上很灵活,因此无需执行拒绝逻辑即可提供合理结果的简单解决方案是 triangular distribution。 IE。您将三角形的下端设置为 1,000,将三角形的上端设置为 20,000,然后将三角形的尖端设置为您想要的平均值 9,000。
上面的维基百科link表示三角分布的均值是:
(a + b + c) / 3
其中 a
和 b
分别是您的下限和上限,c
是您的三角形的尖端。对于您的输入,简单的代数表明 c = 6,000
将给出您想要的平均值 9,000。
C++ 的 <random>
头文件中有一个名为 std::piecewise_linear_distribution
的分布,它非常适合设置三角分布。这只需要两条直线。构建这种三角分布的一种简单方法是:
std::piecewise_linear_distribution<> dist({1000., 6000., 20000.},
[](double x)
{
return x == 6000 ? 1. : 0.;
});
现在您只需将 URNG 插入此发行版并生成结果。为了理智起见,根据您的问题陈述收集一些重要的统计数据也很有帮助,例如最小值、最大值和平均值。
这是一个完整的程序:
#include <algorithm>
#include <iostream>
#include <numeric>
#include <random>
#include <vector>
int
main()
{
std::mt19937_64 eng;
std::piecewise_linear_distribution<> dist({1000., 6000., 20000.},
[](double x)
{
return x == 6000 ? 1. : 0.;
});
std::vector<double> results;
for (int i = 0; i < 100; ++i)
results.push_back(dist(eng));
auto avg = std::accumulate(results.begin(), results.end(), 0.) / results.size();
auto minmax = std::minmax_element(results.begin(), results.end());
std::cout << "size = " << results.size() << '\n';
std::cout << "min = " << *minmax.first << '\n';
std::cout << "avg = " << avg << '\n';
std::cout << "max = " << *minmax.second << '\n';
}
这个应该可移植输出:
size = 100
min = 2353.05
avg = 8972.1
max = 18162.5
如果将采样值的数量增加到足够高,您将看到参数收敛:
size = 10000000
min = 1003.08
avg = 8998.91
max = 19995.5
根据需要播种。
这里有一个
double shiftable_distribution(double minimum, double mean, double maximum)
{
double range = maximum - minimum;
double scaledmean = (mean - minimum) / range;
return pow((double)rand() / RAND_MAX,
(1.0 - scaledmean) / scaledmean)
* range + minimum;
}
实际分布情况(对于 1000、9000、20000)