// int31n也就是下面这个函数, 跟上面Int31n效果是一样的. 但是效率更高.算法不一样. 这个算法非常精彩,效率也更高.
// int31n returns, as an int32, a non-negative pseudo-random number in the half-open interval [0,n).
// n must be > 0, but int31n does not check this; the caller must ensure it.
// int31n exists because Int31n is inefficient, but Go 1 compatibility
// requires that the stream of values produced by math/rand remain unchanged.
// int31n can thus only be used internally, by newly introduced APIs.
//
// For implementation details, see:
// https://lemire.me/blog/2016/06/27/a-fast-alternative-to-the-modulo-reduction
// https://lemire.me/blog/2016/06/30/fast-random-shuffling
func (r *Rand) int31n(n int32) int32 {
v := r.Uint32() //v是通过伪随机算法得到的一个0到2^32次幂之间的一个随机数.
prod := uint64(v) * uint64(n) //得到prod是一个64位随机数.并且是n的倍数.显然因为v最大不超过2^32,所以prod的高32位:int32(prod >> 32)必小于n.
low := uint32(prod) //low是prod的低32位
if low < uint32(n) { //这个函数的算法核心重点:如果low大于等于n,那么我们prod的高32位可以看做1到 [prod/n]*n之间数的均匀采样得到的. 如果low小于n,那么就一定要让prod采样来自于一个n的倍数的区间.这就涉及下面thresh的计算.
thresh := uint32(-n) % uint32(n) // 这个阈值运算的计算思想如下. 涉及到补码的知识. 道理如下: 首先我们v是取的1到2^32之间的随机数. uint32(-n)加上n就得到2^32, thresh是uint32(-n)比n的倍数多出来的数. 所以整个区间1到2^32次幂,我们需要扣除thresh这么多个数, 剩下来的就是n的倍数了.这就符合我们上面一行注释要求的需要一个n的倍数的区间来保证均匀分布性.
for low < thresh { //小于阈值就重新计算.直到保证抽样区间是n的倍数.
v = r.Uint32()
prod = uint64(v) * uint64(n)
low = uint32(prod)
}
}
return int32(prod >> 32)
}
标签:采样,int32,32,int31n,源码,low,go,prod,uint32
From: https://www.cnblogs.com/zhangbo2008/p/18262321