在GPU上有效获取范围内的随机数

时间:2013-06-17 16:31:53

标签: random cuda opencl gpgpu

给定在[0,2 ^ 64]范围内的均匀分布的随机数发生器,是否有任何有效的方法(在GPU上)为范围[0,k]构建随机数发生器,用于某些k <1。 2 ^ 64

一些不起作用的解决方案:

// not uniformly distributed in [0, k)
myRand(rng, k) = rng() % k;

// way too much branching to run efficiently on a gpu
myRand(rng, k) =
    uint64_t ret;
    while((ret = rng() & (nextPow2(k)-1)) >= k);
    return ret;

// only 53 bits of random data, not 64. Also I
// have no idea how to reason about how "uniform"
// this distribution is.
myRand(doubleRng, k) =
    double r = doubleRng(); // generates a random number in [0, 1)
    return (uint64_t)floor(r*k);

如果差异足够小(例如,在1/2 ^ 64之内),我愿意妥协非均匀性。

2 个答案:

答案 0 :(得分:3)

只有两个选项:做模数(或浮点)并确定非均匀性,或者用循环进行拒绝采样。确实没有第三种选择。哪一个更好取决于您的申请。

如果您的 k 通常非常小(例如,您正在洗牌,因此 k 大约为100),那么非均匀性非常小即使在32位也没关系。在64位时,数百万的 k 仍然会给你一个非常小的非均匀性。不,它不会在1/2 ^ 64的数量级,但我无法想象一个真实世界的应用程序,其中大约1/2 ^ 20的非均匀性是显着的。当我为我的RNG库编写测试套件时,我故意针对已知的错误mod实现运行它,即使在32位也很难检测到错误。

如果你真的必须完全统一,那么你只需要采样和拒绝。这可以非常快速地完成,你甚至可以摆脱除法(在拒绝循环之外计算nextPow2() - 这就是我在ojrandlib中的表达方式)。仅供参考,这是制作下一次2次方功率掩模的最快方法:

mask = k - 1;
mask |= mask >> 1;
mask |= mask >> 2;
mask |= mask >> 4;
mask |= mask >> 8;
mask |= mask >> 16;
mask |= mask >> 32;

答案 1 :(得分:0)

如果你有一个返回53位随机数据​​的函数,但你需要64位,请调用它两次,使用第一次调用的最后32位作为结果的前32位,以及底部的32位第二次调用结果的最后32位。如果你的原始功能是统一的,那么这个也是。