使用按位和&而不是模数运算符来随机采样范围内的整数

时间:2013-09-07 15:38:49

标签: c++ random bit-manipulation modulo integer-division

我需要在C ++的[LB,UB]区间内从均匀的整数分布中随机抽样。为此,我从一个“好”的RN发生器(来自Numerical Recipes 3rd ed。)开始,它均匀地随机采样64位整数;我们称之为int64()

使用mod运算符,我可以从[LB,UB]中的整数中采样:

LB+int64()%(UB-LB+1);

使用mod运算符的唯一问题是整数除法的缓慢。所以,我接着尝试了建议here的方法,即:

LB + (int64()&(UB-LB))

按位&方法大约快3倍。这对我来说是巨大的,因为我在C ++中的一个模拟需要随机抽样大约2000万个整数。

但是有一个大问题。当我分析使用按位和&采样的整数时。方法,它们不会在[LB,UB]区间内均匀分布。整数确实来自[LB,UB],但仅>来自该范围内的偶数整数。例如,这里是使用按位&和从[20,50]采样的5000个整数的直方图。方法: Histogram of integers sampled using the bitwise & method

相比之下,这是使用mod运算符方法时类似的直方图,当然可以正常工作: enter image description here

我的bitwise&有什么问题?方法?有没有办法修改它,以便在定义的时间间隔内对偶数和奇数进行采样?

3 个答案:

答案 0 :(得分:2)

如果范围差异(UB-LB)为2 n -1,则效果很好,但如果例如2 n 则无法正常工作。

答案 1 :(得分:2)

按位&运算符查看其操作数的每对相应位,仅使用这两位执行and,并将该结果放入结果的相应位。

因此,如果UB-LB的最后一位为0,则结果的最后一位为0。也就是说,如果UB-LB是偶数,那么每个输出都是偶数。

&不适合此目的,除非UB-LB+1是2的幂。如果你想找到一个模数,那么就没有一般的捷径:编译器已经实现{{1}它知道的最快的方式。

请注意,我没有说一般快捷方式。对于编译时已知的%的特定值,可以有更快的方法。如果你能以某种方式安排UB-LBUB具有编译器在编译时可以计算的值,那么当你编写LB时它将使用它们。

顺便说一句,使用%实际上并不会在整个范围内产生均匀分布的整数,除非范围的大小是2的幂。否则必须有一些偏向某些值的偏差,因为%函数的范围无法在所需范围内平均分配。可能是偏差太小而不能特别影响您的模拟,但是糟糕的随机数生成器在过去已经破坏了随机模拟,并且会再次这样做。

如果您希望在任意范围内均匀随机数分布,请使用C ++ 11中的int64()或Boost中相同名称的类。

答案 2 :(得分:1)

只有当间隔的大小是2的幂时,两者才是等价的。通常y%x和y&(x-1)不相同。

例如,x%5产生的数字从0到4(或者为-4,对于负x),但x& 4产生0或4,从不产生1,2或3,因为按位运算符的工作方式如何...