是否反复向随机数生成器播种一个合理的哈希函数

Is repeatedly seeding a random number generator a reasonable hash function?

本文关键字:一个 函数 哈希 随机数生成器 是否      更新时间:2023-10-16

我希望生成大量随机数据,这些数据对于给定的key是可重复的,包括数字列表:

[a, b, c, d, e, ...]

下面是一个好的或明智的方法来让RNG进入生成随机数据的状态,以这样的方式,对于每个n元组[a, b, c, ..., n],该数据与"相邻"n元组[a+1, b, c, ..., n], [a, b+1, c, ..., n]等的输出不相关。

srand(a);
srand(rand() * b);
srand(rand() * c);
...
srand(rand() * n);
# generate random data:
for (int i=0; i < 100; +i)
  printf("%d", rand());

我认为这个问题归结为以下内容:rand_hash是2元组(a, b)的好哈希函数吗?

int rand_hash(int a, int b) { 
  srand(a); 
  srand(rand() * b); 
  return rand();
}
注:我并不想暗示srandrand是RNG的任何特定实现。为了便于讨论,假设我们使用的是一个很好的梅森扭转码。

Edit:如果不清楚的话,"合理的哈希函数"是指以下内容。在二元组[a, b]的限制情况下,rand_hash的输出应该在int的范围内是一致的,并且(通常)ab的变化幅度与返回值的变化幅度之间不应该存在相关性。

不,这不是一个合理的方法。

  1. 你不知道rand的实现是什么。随机数生成器的设计目的是在几个生成的随机数的周期内提供近似均匀分布的数字。它们不是为了在一组(32位)种子上提供均匀分布的数字而设计的。在您假设的mersenne_twister的情况下,随机数生成器的状态比您提供给srand(特别是624*sizeof(int))的整数大得多。RNG确保其输出是随机和均匀的大部分能力都来自于那个额外的状态,而你拿走了它。(种子只能是2^32个状态中的一个)
  2. 如果你曾经升级你的编译器或库或类似的东西,任何你可能已经序列化到磁盘将变得不可读。(如果rand是一个黑盒,没有人说明天的实现与今天的实现匹配)。
  3. 对于相同的srand输入,哈希函数的输出返回相同的东西。因此,您已经有了一个散列——srand的输入。对于给定的srand输入,RNG生成相同的输出。因此,您可能获得的哈希数不大于返回您已经计算过的哈希值。如果您的初始散列到srand的散列对于散列表来说分布不好,那么请适当地扩展散列,以便它在您的表中表现良好。
  4. 对于rand的一些实现,这执行得非常差。考虑一个线性同余生成器(这在C库中更常见,因为它的状态为sizeof(int)——例如BSD生成器)。LCG的格式为xNext = a*xCurrent + b。考虑:

    static int seed = 0;
    void srand(int newSeed)
    {
        seed = newSeed;
    }
    int rand()
    {
        seed = (int) ((1103515245 * ((unsigned int)seed) + 12345) & 0x7fffffffUL); 
        return seed;
    }
    

    请注意,这种(常见)类型的生成器生成的哈希值很容易与您的输入值相关联。

如何使用boost::hash_combine http://www.boost.org/doc/libs/1_33_1/doc/html/hash_combine.html创建您的初始种子?使用srand不止一次总是在我的脑海中触发危险信号。

潜在问题:

如果另一个线程在你的哈希函数中间调用rand()怎么办?