C++uniform_int_distribution在第一次调用时总是返回min()
C++ uniform_int_distribution always returning min() on first invocation
在标准库的至少一个实现中,std::uniform_int_distribution<>
的第一次调用不会返回随机值,而是返回分布的最小值。也就是说,给定代码:
default_random_engine engine( any_seed() );
uniform_int_distribution< int > distribution( smaller, larger );
auto x = distribution( engine );
assert( x == smaller );
对于any_seed()
、smaller
或larger
的任何值,x
实际上将是smaller
。
要在家里玩,您可以尝试在gcc 4.8.1中演示这个问题的代码示例。
我相信这不是正确的行为?如果这是正确的行为,为什么随机分布会返回这个明显的非随机值?
观察到的行为的说明
如果可能结果的范围小于rng产生的数字范围,uniform_int_distribution
就是这样将随机位映射为数字的:
const __uctype __uerange = __urange + 1; // __urange can be zero
const __uctype __scaling = __urngrange / __uerange;
const __uctype __past = __uerange * __scaling;
do
__ret = __uctype(__urng()) - __urngmin;
while (__ret >= __past);
__ret /= __scaling;
其中__urange
是larger - smaller
并且__urngrange
是rng可以返回的最大值和最小值之间的差。(代码来自libstdc++6.1中的bits/uniform_int_dist.h)
在我们的例子中,rngdefault_random_engine
是minstd_rand0
,它在您测试的[0,10]范围内产生__scaling == 195225785
。因此,如果rng() < 195225785
,则分布将返回0。
minstd_rand0
返回的第一个数字是
(16807 * seed) % 2147483647
(其中seed == 0
被调整为1
btw)。因此,我们可以看到,使用您使用的uniform_int_distribution< int > distribution( 0, 10 );
,由编号小于11615的minstd_rand0
种子产生的第一个值将产生0。(由于我的一个错误而被修改。;))
你提到了更大的种子会消失的问题:一旦种子变得足够大,可以真正让mod操作做一些事情,我们就不能简单地通过除法将整个范围的值分配给相同的输出,所以结果会看起来更好。
这是否意味着(libstdc++的impl of)<随机>坏了吗
没有。你总是选择一个小的随机32位种子,从而在这个种子中引入了显著的偏差。结果中出现的这种偏见并不奇怪,也不邪恶。对于随机种子,即使是minstd_rand0
也会产生一个相当均匀的随机第一个值。(尽管之后的数字序列不会有很好的统计质量。)
我们该怎么办
情况1:您想要高统计质量的随机数。
为此,您可以使用一个更好的rng,如mt19937
,并为其整个状态空间播种。对于Mersenne Twister,这是624个32位整数。(作为参考,以下是我在回答中提出的一些有用的建议,试图正确地做到这一点。)
情况2:您真的只想使用那些小种子。
我们仍然可以从中获得不错的结果。问题是伪随机数生成器通常依赖于"伪随机数";有点连续";在他们的种子上。为了绕过这一点,我们丢弃了足够多的数字,让最初相似的输出序列发散。因此,如果你的种子必须很小,你可以这样初始化你的rng:
std::mt19937 rng(smallSeed);
rng.discard(700000);
这是至关重要的,你使用一个好的rng像梅森扭曲。我不知道有什么方法可以从一个种子不好的minstd_rand0
中获得甚至不错的值,例如,看到这个火车残骸。即使适当地播种,mt19937
的统计特性到目前为止也是优越的。
你有时听到的对大状态空间或慢生成的担忧,在嵌入式世界之外通常是无关紧要的。根据boost和cacert.at,MT甚至比minstd_rand0
快得多。
尽管如此,你仍然需要做丢弃的把戏,即使你的结果在没有丢弃的情况下肉眼看起来很好。这在我的系统上只需要不到一毫秒的时间,而且你不经常播种rng,所以没有理由不这样做。
请注意,我无法给你一个我们需要的丢弃数量的精确估计,我从这个答案中得出了这个值,它将本文联系起来是合理的。我现在没有时间解决这个问题。
- 来自 std::list 的迭代器 .end() 按预期返回"0xcdcdcdcdcdcdcdcd"但 .begin()
- 什么时候在C++中返回常量引用是个好主意
- 你能重载对象变量名本身返回的内容吗
- 为什么 Serial.println(<char[]>);返回随机字符?
- C++映射:具有自定义类的运算符[]不起作用(总是返回0)
- 如何获取std::result_of函数的返回类型
- QueryWorkingSet总是返回false
- (C++)分析树以计算返回错误值的简单算术表达式
- 访问者访问变体并返回不同类型时出错
- 如何返回一个类的两个对象相加的结果
- 如何使用std::min和std::less返回对象
- 为什么将 1 添加到 numeric_limits<float>::min() 返回 1?
- 函数读取最大和min int值,并用文本字符串返回
- C++uniform_int_distribution在第一次调用时总是返回min()
- 返回最小值的 min() 的简洁实现
- 是 std::min 返回一个 lval 标准
- 当min和max为正数时,Rand()返回负数
- 为什么numeric_limits::min对int返回负值,而对float/double返回正值?
- 返回键值在MIN和MAX之间的子映射
- 如何摆脱内部编译错误:非法指令min() _GLIBCXX_USE_NOEXCEPT{返回__FLT_MIN__;}