是否反复为随机数生成器播种合理的散列函数?

时间:2011-10-28 18:45:38

标签: c++ c cryptography

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

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

以下是将RNG置于生成随机数据的状态的良好或明智的方法,使得对于每个n元组[a, b, c, ..., n],该数据与“相邻”的输出不相关“n-tuples [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的任何特定实现。假设为了论证我们正在使用一个好的Mersenne Twister代码。

编辑:如果不清楚,通过“合理的哈希函数”,我的意思是以下内容。在2元组[a, b]的受限情况下,rand_hash的输出应该在int的范围内是均匀的,并且(通常)在幅度之间应该没有相关性。 ab的变化以及返回值变化的幅度。

3 个答案:

答案 0 :(得分:9)

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

  1. 您不知道rand的实施是什么。随机数发生器被设计成在几个生成的数字的周期内提供近似均匀分布的数字。它们的设计不是为了在(32位)种子集上提供均匀分布的数字。在您假设的mersenne_twister情况下,随机数生成器的状态远大于您提供给srand的整数(具体地,624*sizeof(int))。 RNG必须确保其输出的大部分功率是随机和均匀的,来自该附加状态,并且你把它拿走了。 (种子只能是2 ^ 32个状态中的一个)
  2. 如果您升级了编译器或库或类似的东西,那么您可能序列化到磁盘的任何内容都将变得不可读。 (如果rand是一个黑盒子,没有人说明天的实施与今天相符。)
  3. 您的散列函数的输出会为srand的相同输入返回相同的内容。因此,您已经有一个哈希 - srand的输入。 RNG为srand的给定输入生成相同的输出。因此,您可能获得的哈希数不会超过返回您已经计算过的哈希值。如果您对srand的初始哈希值对于哈希表的分布很差,那么请适当地缩放哈希值,使其在表中表现良好。
  4. 对于rand的某些实现,这表现得非常糟糕。考虑linear congruential生成器(C库更常见,因为它具有sizeof(int)状态 - 例如the BSD generator)。 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;
    }
    

    请注意,此(常见)类型的生成器会生成易于与输入值相关的哈希值。

答案 1 :(得分:2)

使用boost::hash_combine http://www.boost.org/doc/libs/1_33_1/doc/html/hash_combine.html创建初始种子怎么样?不止一次使用srand总会在我的脑海中触发红旗。

答案 2 :(得分:1)

潜在问题:

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