当你在一个集合上运行这些例程时会发生什么?你有偏见吗
像 rand()?
答案是:这取决于生成器返回的范围大小与模运算中的除数之间的关系。如果除数不均匀地划分范围,则分布将偏斜。偏置比在 [1, 2] 范围内,其中 1 表示没有偏置(对于均匀分布),偏置随除数增加。关于arcrandom4(),这转化为在模除数不是 2^32 的偶数除数的所有情况下获得的偏态分布。下面解释其背后的基本原理。
介绍。偏见
假设我们正在尝试使用 [0, 99] 模拟区间内的均匀 int 分布
int x = rand() % 100;
运算符 % 使 X 的概率分布发生倾斜,因为 rand() 的最大值 RAND_MAX 不能等于 k 100 + 99。这导致如果您想象 0-RAND_MAX 范围的所有 100 长度部分那么您可以看到最后一部分可能不会产生完整的 0-99。因此,您有更多的数字可以生成 0, 1, 2..., p 但不是必需的 p + 1, ..., 98, 99 (0、1、2、...、p 中的每个数字再出现 1 次) )。这种方法的不准确性会随着不均匀划分范围的除数越大而增加,并且与均匀分布相比,最大偏差等于 2。
在下面的部分中,我们展示了作为从 [ 0, p] 获得数字的概率与从 [ p + 1, n] 获得数字的概率之比来衡量的偏差等于 ( k + 1 ) / k,我们用 2 个例子来证实这一点。
公式
我们将展示模运算引入的偏差究竟是什么(应用于均匀分布生成器以修剪输出范围的运算)。我们将按照公式进行操作
x = rand() % ( n + 1)
其中rand() 是某个生成器,( n + 1) 是模运算中的除数。下图显示了我们的立场:
我们可以看到[ 0, n] 范围内的数字如何在一次试验中分为重复k + 1 次(数字[ 0, p])和重复k 次(数字[ p + 1, n])的数字,即“从x = rand() % (n+1)获得的分布中取数”。 p 定义为生成器给出的最大数(即 Rand_MAX)除以所需范围的大小(n + 1)时的余数:
p = ( N - 1) % ( n + 1)
N - 1 = k * (n + 1) + p
k是商
k = ( N - 1 - p) / ( n + 1)
在一个单独的试验中有
( p + 1) * ( k + 1) + ( n - p) * k =
= p + 1 + k( n + 1) = N
可能的结果。因此接收到重复 k 次的元素的概率是 k / N。让我们表示
f_0 = ( k + 1) / N, [ 0, p]中每个元素的概率
f_1 = k / N, [p + 1, n]中每个元素的概率
假设我们将从这个采样的偏差表达为均匀分布上的变换分布为属于[ 0, p]的元素概率与来自范围@的元素概率之比987654338@:
偏差 = f_0 / f_1 = ( k + 1) / k
那么,数字的频率是两倍吗?
没有。当我们查看图片数字重复时,这一事实并不意味着比率为 2。如果生成器的范围恰好被划分为 2 个子范围,则该比率只是一种特殊情况。一般情况下,偏置比为(k + 1) / k,当除数n + 1趋于1,(且k趋于N)时,偏置比逐渐减小。
示例
我们现在考虑两个简单的例子(正如@dyp 所建议的那样)。首先,我们将从给定的分布中生成 1000 * 1000 个样本
x = rand() % m
生成器为 std::uniform_int_distribution<> dist(0, 19),除数 m = n + 1 等于 15,下一个等于 6。
示例 1
int x = rand() % 15; // n + 1 = 15, rand is uniform distribution over [0,19]
测试程序是:
#include <iostream>
#include <random>
#include <vector>
int main()
{
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<> dist(0, 19);
std::vector<int> v(15);
const int runs = 1000 * 1000;
for (int i = 0; i < runs; ++i)
{
++v[dist(mt) % v.size()];
}
for (int i = 0; i < v.size(); ++i)
{
std::cout << i << ": " << v[i] << "\n";
}
}
code
结果:
0: 100500
1:100016
2:99724
3:99871
4:99936
5:50008
6:49762
7:50023
8:50123
9: 49963
10: 50117
11:50049
12: 49885
13: 49760
14: 50263
我们可以看到,在这种情况下,[ 0, p] = [ 0, 4] 范围内的数字出现的频率大约是其余数字的两倍。这符合我们的偏差公式
偏差 = f_0 / f_1 = ( k + 1) / k = 2 / 1
示例 2
int x = rand() % 6; // n + 1 = 6, rand is uniform distribution over [0,19]
测试程序是:
#include <iostream>
#include <random>
#include <vector>
int main()
{
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<> dist(0, 19);
std::vector<int> v(6);
const int runs = 1000 * 1000;
for (int i = 0; i < runs; ++i)
{
++v[dist(mt) % v.size()];
}
for (int i = 0; i < v.size(); ++i)
{
std::cout << i << ": " << v[i] << "\n";
}
}
code
结果:
0: 199875
1:199642
2:149852
3:149789
4:150237
5:150605
在这种情况下,我们观察到 [ 0, p] = [ 0, 1] 范围内的数字出现的频率不是其他数字的两倍,而是大约 20/15。实际上这是 4/3,因为我们在这种情况下的偏差公式是
偏差 = f_0 / f_1 = ( k + 1) / k = 4 / 3
下图有助于理解这一结果。
full code