C/C++ rand() 函数用于有偏差的期望
C/C++ rand() function for biased expectation
我正在使用 <stdlib.h> rand()
函数生成 [0 ... 9] 范围内的 100 个随机整数。我用下面的方法来生成它们的平均分布,
int random_numbers[100];
for(register int i = 0; i < 100; i++){
random_numbers[i] = rand() % 10;
}
这工作正常。但现在我想得到 100 个数字,我希望这些数字中的大约 50% 是 5。我该怎么做?
扩展问题
我想得到100个号码。如果我想要这些数字的 50% 在 0~2 之间怎么办?我的意思是这些数字的 50% 将只包含数字 0、1、2。如何做到这一点?
我期待可以在 10 或 100 的边界之外应用的通用步骤。
嗯嗯,在0
和17
之间随机取一个数怎么样,如果大于9
,就改成5
?
对于 0 - 17,你会得到这样的分布
0,1,2,3,4,5,6,7,8,9,5,5,5,5,5,5,5,5
代码:
int random_numbers[100];
for(register int i = 0; i < 100; i++){
random_numbers[i] = rand() % 18;
if (random_numbers[i] > 9) {
random_numbers[i] = 5;
}
}
你基本上添加了一组超出你想要的范围的数字,当翻译成 5
给你相等数量的 5
和 non-5
。
为了让 大约 这些数字的 50% 在 [0, 2]
范围内,您可以将 rand()
的整个范围分成相等的两半然后使用相同的基于 %
的技术将前半部分映射到 [0, 2]
范围,将后半部分映射到 [3, 9]
范围。
int random_numbers[100];
for(int i = 0; i < 100; i++)
{
int r = rand();
random_numbers[i] = r <= RAND_MAX / 2 ? r % 3 : r % 7 + 3;
}
要使这些数字中的 大约 达到 5
的 50%,可以使用类似的技术。只需将下半部分映射到 [0, 9]
范围,排除 5
int random_numbers[100];
for(int i = 0; i < 100; i++)
{
int r = rand();
if (r <= RAND_MAX / 2)
r = 5;
else if ((r %= 9) >= 5)
++r;
random_numbers[i] = r;
}
我认为使用其他答案提到的技术很容易解决 50% 的特定问题。让我们尝试回答一般情况下的问题 -
假设你想要一个分布,其中你想要数字 {A1, A2, .. An}
和百分比 {P1, P2, .. Pn}
并且 Pi
的总和是 100%
(所有百分比都是整数,如果不是的话可以调整)。
我们将创建一个大小为 100 的数组并用数字 A1-An
填充它。
int distribution[100];
现在我们填写每个数字,它是次数的百分比。
int postion = 0;
for (int i = 0; i < n; i++) {
for( int j = 0; j < P[i]; j++) {
// Add a check here to make sure the sum hasn't crossed 100
distribution[position] = A[i];
position ++;
}
}
现在这个初始化已经完成了一次,你可以抽取一个随机数作为 -
int number = distribution[rand() % 100];
如果您的百分比不是整数,但您想要 0.1%
的精度,您可以创建 1000
的数组而不是 100
。
在这两种情况下,目标都是从一组中选择 50%,从另一组中选择 50%。代码可以调用 rand()
并使用一些位(一个)来选择组,其余位用于值选择。
如果需要的数字范围远小于RAND_MAX
,第一次尝试可以使用:
int rand_special_50percent(int n, int special) {
int r = rand();
int r_div_2 = r/2;
if (r%2) {
return special;
}
int y = r_div_2%(n-1); // 9 numbers left
if (y >= special) y++;
return y;
}
int rand_low_50percent(int n, int low_special) {
int r = rand();
int r_div_2 = r/2;
if (r%2) {
return r_div_2%(low_special+1);
}
return r_div_2%(n - low_special) + low_special + 1;
}
样本
int r5 = rand_special_50percent(10, 5);
int preferred_low_value_max = 2;
int r012 = rand_low_50percent(10, preferred_low_value_max);
高级:
n
高于 RAND_MAX/2
,需要额外调用 rand()
。
当使用 rand()%n
时,除非 (RAND_MAX+1u)%n == 0
(n
是 RAND_MAX+1
的约数),否则会引入偏差。上面的代码没有对此进行补偿。
C++11 解决方案(不是最优但简单)
std::piecewise_constant_distribution
可以为给定的区间和每个区间的权重生成随机实数(浮点数或双精度数)。
不是最优的,因为这个解决方案正在生成双精度并将双精度转换为整数。另外 不能保证从 [0,3) 100 个样本中准确地得到 50,但是保证大约 50 个样本。
对于您的情况:2 个间隔 - [0,3), [3,100)
及其权重 [1,1]
权重相等,因此来自 [0,3)
的约 50% 的数字和来自 [3,100)
的约 50% 的数字
#include <iostream>
#include <string>
#include <map>
#include <random>
int main()
{
std::random_device rd;
std::mt19937 gen(rd());
std::vector<double> intervals{0, 3, 3, 100};
std::vector<double> weights{ 1, 0, 1};
std::piecewise_constant_distribution<> d(intervals.begin(), intervals.end(), weights.begin());
std::map<int, int> hist;
for(int n=0; n<100; ++n) {
++hist[(int)d(gen)];
}
for(auto p : hist) {
std::cout << p.first << " : generated " << p.second << " times"<< '\n';
}
}
Output:
0 : generated 22 times
1 : generated 19 times
2 : generated 16 times
4 : generated 1 times
5 : generated 2 times
8 : generated 1 times
12 : generated 1 times
17 : generated 1 times
19 : generated 1 times
22 : generated 2 times
23 : generated 1 times
25 : generated 1 times
29 : generated 1 times
30 : generated 2 times
31 : generated 1 times
36 : generated 1 times
38 : generated 1 times
44 : generated 1 times
45 : generated 1 times
48 : generated 1 times
49 : generated 1 times
51 : generated 1 times
52 : generated 1 times
53 : generated 1 times
57 : generated 2 times
58 : generated 3 times
62 : generated 1 times
65 : generated 2 times
68 : generated 1 times
71 : generated 1 times
76 : generated 2 times
77 : generated 1 times
85 : generated 1 times
90 : generated 1 times
94 : generated 1 times
95 : generated 1 times
96 : generated 2 times
我正在使用 <stdlib.h> rand()
函数生成 [0 ... 9] 范围内的 100 个随机整数。我用下面的方法来生成它们的平均分布,
int random_numbers[100];
for(register int i = 0; i < 100; i++){
random_numbers[i] = rand() % 10;
}
这工作正常。但现在我想得到 100 个数字,我希望这些数字中的大约 50% 是 5。我该怎么做?
扩展问题
我想得到100个号码。如果我想要这些数字的 50% 在 0~2 之间怎么办?我的意思是这些数字的 50% 将只包含数字 0、1、2。如何做到这一点?
我期待可以在 10 或 100 的边界之外应用的通用步骤。
嗯嗯,在0
和17
之间随机取一个数怎么样,如果大于9
,就改成5
?
对于 0 - 17,你会得到这样的分布
0,1,2,3,4,5,6,7,8,9,5,5,5,5,5,5,5,5
代码:
int random_numbers[100];
for(register int i = 0; i < 100; i++){
random_numbers[i] = rand() % 18;
if (random_numbers[i] > 9) {
random_numbers[i] = 5;
}
}
你基本上添加了一组超出你想要的范围的数字,当翻译成 5
给你相等数量的 5
和 non-5
。
为了让 大约 这些数字的 50% 在 [0, 2]
范围内,您可以将 rand()
的整个范围分成相等的两半然后使用相同的基于 %
的技术将前半部分映射到 [0, 2]
范围,将后半部分映射到 [3, 9]
范围。
int random_numbers[100];
for(int i = 0; i < 100; i++)
{
int r = rand();
random_numbers[i] = r <= RAND_MAX / 2 ? r % 3 : r % 7 + 3;
}
要使这些数字中的 大约 达到 5
的 50%,可以使用类似的技术。只需将下半部分映射到 [0, 9]
范围,排除 5
int random_numbers[100];
for(int i = 0; i < 100; i++)
{
int r = rand();
if (r <= RAND_MAX / 2)
r = 5;
else if ((r %= 9) >= 5)
++r;
random_numbers[i] = r;
}
我认为使用其他答案提到的技术很容易解决 50% 的特定问题。让我们尝试回答一般情况下的问题 -
假设你想要一个分布,其中你想要数字 {A1, A2, .. An}
和百分比 {P1, P2, .. Pn}
并且 Pi
的总和是 100%
(所有百分比都是整数,如果不是的话可以调整)。
我们将创建一个大小为 100 的数组并用数字 A1-An
填充它。
int distribution[100];
现在我们填写每个数字,它是次数的百分比。
int postion = 0;
for (int i = 0; i < n; i++) {
for( int j = 0; j < P[i]; j++) {
// Add a check here to make sure the sum hasn't crossed 100
distribution[position] = A[i];
position ++;
}
}
现在这个初始化已经完成了一次,你可以抽取一个随机数作为 -
int number = distribution[rand() % 100];
如果您的百分比不是整数,但您想要 0.1%
的精度,您可以创建 1000
的数组而不是 100
。
在这两种情况下,目标都是从一组中选择 50%,从另一组中选择 50%。代码可以调用 rand()
并使用一些位(一个)来选择组,其余位用于值选择。
如果需要的数字范围远小于RAND_MAX
,第一次尝试可以使用:
int rand_special_50percent(int n, int special) {
int r = rand();
int r_div_2 = r/2;
if (r%2) {
return special;
}
int y = r_div_2%(n-1); // 9 numbers left
if (y >= special) y++;
return y;
}
int rand_low_50percent(int n, int low_special) {
int r = rand();
int r_div_2 = r/2;
if (r%2) {
return r_div_2%(low_special+1);
}
return r_div_2%(n - low_special) + low_special + 1;
}
样本
int r5 = rand_special_50percent(10, 5);
int preferred_low_value_max = 2;
int r012 = rand_low_50percent(10, preferred_low_value_max);
高级:
n
高于 RAND_MAX/2
,需要额外调用 rand()
。
当使用 rand()%n
时,除非 (RAND_MAX+1u)%n == 0
(n
是 RAND_MAX+1
的约数),否则会引入偏差。上面的代码没有对此进行补偿。
C++11 解决方案(不是最优但简单)
std::piecewise_constant_distribution
可以为给定的区间和每个区间的权重生成随机实数(浮点数或双精度数)。
不是最优的,因为这个解决方案正在生成双精度并将双精度转换为整数。另外 不能保证从 [0,3) 100 个样本中准确地得到 50,但是保证大约 50 个样本。
对于您的情况:2 个间隔 - [0,3), [3,100)
及其权重 [1,1]
权重相等,因此来自 [0,3)
的约 50% 的数字和来自 [3,100)
#include <iostream>
#include <string>
#include <map>
#include <random>
int main()
{
std::random_device rd;
std::mt19937 gen(rd());
std::vector<double> intervals{0, 3, 3, 100};
std::vector<double> weights{ 1, 0, 1};
std::piecewise_constant_distribution<> d(intervals.begin(), intervals.end(), weights.begin());
std::map<int, int> hist;
for(int n=0; n<100; ++n) {
++hist[(int)d(gen)];
}
for(auto p : hist) {
std::cout << p.first << " : generated " << p.second << " times"<< '\n';
}
}
Output:
0 : generated 22 times
1 : generated 19 times
2 : generated 16 times
4 : generated 1 times
5 : generated 2 times
8 : generated 1 times
12 : generated 1 times
17 : generated 1 times
19 : generated 1 times
22 : generated 2 times
23 : generated 1 times
25 : generated 1 times
29 : generated 1 times
30 : generated 2 times
31 : generated 1 times
36 : generated 1 times
38 : generated 1 times
44 : generated 1 times
45 : generated 1 times
48 : generated 1 times
49 : generated 1 times
51 : generated 1 times
52 : generated 1 times
53 : generated 1 times
57 : generated 2 times
58 : generated 3 times
62 : generated 1 times
65 : generated 2 times
68 : generated 1 times
71 : generated 1 times
76 : generated 2 times
77 : generated 1 times
85 : generated 1 times
90 : generated 1 times
94 : generated 1 times
95 : generated 1 times
96 : generated 2 times