关于std::mt19937_64取模分布及std::uniform_int_distribution必要性的问询
问题解答:直接取模vs std::uniform_int_distribution
好问题!先直接给结论:你代码里用generator() % size得到的是近似均匀分布,但并非严格均匀;而std::uniform_int_distribution的作用就是帮你实现真正无偏差的均匀整数分布。
为什么直接取模不是严格均匀?
std::mt19937_64生成的随机数范围是[0, 2^64 - 1],假设你要取模的size不是2的整数次幂(比如你的例子里size=100000),那么2^64除以size会有余数r:
2^64 = k * size + r (其中0 < r < size)
这意味着:
- 对于
0到r-1的结果,每个值会被映射到k+1次生成器的输出(因为前r*size个生成器值里,每个余数对应k+1次) - 对于
r到size-1的结果,每个值只会被映射到k次
虽然当size很大时(比如100000),这个偏差非常小,肉眼看输出的星号分布几乎均匀,但从统计角度看,小数值的出现概率确实略高于大数值。如果你的场景对随机性要求很高(比如密码学、高精度模拟),这种偏差会带来问题。
举个更直观的小例子:假设生成器只能输出0-9的数,size=3,那么:
- 0、1、2分别对应生成器的0/3/6、1/4/7、2/5/8(各3次),还有生成器的9会被模成0,所以0总共出现4次,概率4/10,而1和2是3/10,偏差很明显。
为什么需要std::uniform_int_distribution?
std::uniform_int_distribution专门解决了这个问题:
- 无偏差的均匀分布:它会通过拒绝采样的方式过滤掉生成器输出中会导致偏差的部分——如果生成的数大于等于
k*size(也就是超出了能被size整除的范围),就丢弃这个数,重新生成,直到得到符合要求的数。这样每个结果的出现概率完全相等。 - 代码可读性更强:直接写
std::uniform_int_distribution<int> dist(0, size-1);,能清晰表达你想要生成0到size-1之间均匀分布整数的意图,比取模的写法更直观。
修正后的代码示例
把你的代码改成用std::uniform_int_distribution的写法:
#include <iostream> #include <map> #include <random> int main() { std::mt19937_64 generator(134); std::uniform_int_distribution<int> dist(0, 99999); // 对应size=100000的范围 std::map<int, int> freq; const int size = 100000; for (int i = 0; i < size; ++i) { int r = dist(generator); freq[r]++; } for (auto f : freq) { std::cout << std::string(f.second, '*') << std::endl; } return 0; }
内容的提问来源于stack exchange,提问作者cmutex
相关产品推荐
相关产品推荐

