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 的边界之外应用的通用步骤。

嗯嗯,在017之间随机取一个数怎么样,如果大于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 给你相等数量的 5non-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 == 0nRAND_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