使用Boost PRNG创建一个巨大的随机数查找表

时间:2013-06-26 19:53:50

标签: c++ boost random

我正在尝试使用Boost的正态分布来生成给定不同种子的随机数。换句话说,我需要为seed1,seed2等生成相同的随机数;数千种子将在模拟过程中传递给函数。随机数生成器永远不会被使用。 [编辑:“密钥”是一个比“种子”更好的词 - 请参阅下面的最终描述块。] 我不确定生成单个RNG并重新种植它是否最有意义(如果是这样,怎么样)或者每次生成一个新的更容易。这是我到目前为止所涉及的,它涉及在每次请求随机正常数字时构建一个新的种子rng:


double rnorm( int thisSeed ) {
  boost::mt19937 rng( thisSeed );
  boost::normal_distribution<> nd( 0.0, 1.0 ); // (mean, sd)
  boost::variate_generator > var_nor( rng, nd );
  return var_nor();
}

这是愚蠢的吗?我是PRNG的新手,特别是Boost的实施。


更全面地描述我为什么要这样做:

我正在创建一个巨大的随机能量景观来模拟蛋白质相互作用:每个序列都有一个特定的能量,计算为淬火高斯随机数的总和,取决于特定位置特定氨基酸的值(以及一些其他序列)属性)。我想使用PRNG来计算这些伪随机值是什么:这些值必须是一致的(相同的序列应该产生相同的值),但是有太多的东西要存储。举一个简单的例子,我可能有一个序列ARNDAMR并根据两个子能量计算其总能量:一个是随机正常数,它取决于位置1中的A和位置4中的D,而另一个子能量是一个随机数,取决于最后三个氨基酸。我正在将配置转换为密钥,用作PRNG的种子(参数)。将构建和变异数千个序列,因此我需要一种快速计算能量的方法 - 所以我需要知道如何最好地播种和调用我的RNG。除了这些能量值“查找”之外,我不会将Boost RNG用于其他任何东西。


进一步(tl; dr)解释:

我将拥有1到10 ^ 6或10 ^ 7之间整数的“键”值。我希望每个人都映射到高斯随机数。键值与其数字之间不应存在任何互相关(例如,键145-148不应映射到自相关的“随机”数字)。

每次在模拟中调用它(键)时,我需要一个给定的键来返回相同的随机数。我不想将密钥随机数对存储在查找表中。

1 个答案:

答案 0 :(得分:2)

您的方法基本上误解了PRNG的工作方式。如果你重新使用每次使用,那么你根本就不会得到随机数,你只会获得种子的错误哈希函数。特别是,即使您正在调用PRNG的正态分布函数,您的数字也不会正常分布,因为PRNG只保证从特定种子生成的随机数是正常的。

如果你需要一组大量的随机数可以对一组特定的输入进行重复,那么就生成一个单独的数字,这个数字是那些输入的函数,然后用PRNG播种,然后从PRNG获得可预测的数字序列;它将为相同的输入产生相同的序列,并且数字将由PRNG正确分发。

如果用于确定随机序列的输入集很大(特别是大于PRNG种子大小的输入),那么每组输入都不会有唯一的序列。这可能适用于您的应用程序,或者您可能希望使用具有较大种子的PRNG。

查看我的公共域ojrandlib。它使用大种子,并使用快速Ziggurat算法生成正态分布的数字。


看到您的澄清后编辑:

啊,现在我明白了。没有“a”高斯随机这样的东西。分布只对一个种子的整个序列有意义,所以你需要做的是创建和播种单个生成器,然后从你的每个密钥N的那个生成器中获取第N个随机值。如果你不做这个顺序(也就是说,如果你完全随机地从键中取出而不是作为序列的一部分),这将非常缓慢,但仍然可能。您可能想要查看是否可以强制执行序列,比如在获取它们之前对其进行排序。

ojrandlib也有一个函数discard(),所以如果你需要在一个序列中找到第1,000,000个数字,你可以为PRNG播种并丢弃它们中的999,999,这比实际生成它们要快,但仍然会很慢。

可能更好:不是使用你的密钥来种子高斯生成器,而是计算密钥+固定种子的良好散列函数(这将导致均匀分布的随机位),然后将这些散列位解释为两个均匀浮点数,然后使用Box-Muller或Ziggurat来改变分布。这样,你获得的数字将全部来自相同的“种子”(这是哈希的输入),但通常是分布式的。你不需要一个加密安全的哈希,所以像MurMurHash这样的东西可能效果很好,尽管为了这么特殊的目的你可能会更好地自己动手。


我图书馆的用户可能会遇到与您类似的问题,所以我研究了一些可能性。以下是一些可能适合您的代码:

/* Thomas Wang's 32-bit integer hash */
uint32_t nth_rand32(uint32_t a) {
    a -= a << 6;
    a ^= a >> 17;
    a -= a << 9;
    a ^= a << 4;
    a -= a << 3;
    a ^= a << 10;
    a ^= a >> 15;
    return a;
}

/* Marsaglia polar method */
double nth_normal(int index) {
    double f, g, w;
    int skip = 0;
    uint64_t x, y;

    do {
        x = (uint64_t)nth_rand32((index & ~1) + skip);
        y = (uint64_t)nth_rand32((index | 1) + skip);
        skip += 0x40000001;

        x = (x << 20) | 0x3ff0000000000000ull;
        f = *(double *)(&x) * 2.0 - 3.0;
        y = (y << 20) | 0x3ff0000000000000ull;
        g = *(double *)(&y) * 2.0 - 3.0;

        w = f * f + g * g;
    } while (w >= 1.0 || w == 0.0);

    w = sqrt((-2.0 * log(w)) / w);

    if (index & 1) w *= f;
    else w *= g;
    return w;
}

哈希没有通过死硬,但它非常好。我生成了10,000,000个随机法线,并获得了此分布(如果此图像上传有效):

Distribution

不完美,但也不是太糟糕。使用更昂贵的哈希会更好,但我会让你决定速度/准确性权衡的对象。