rand() 函数是否总是使用相同的种子产生相同的结果?

Will the rand() function ALWAYS produce the same result with the same seed?

我正在尝试在 C 中实现一组编码和解码隐写术函数,其中我使用 rand() 将我的数据随机散布在一个数组中。

我使用 rand 来计算随机索引,如下所示:

unsigned int getRandIndex(int size) {
  return ((unsigned)(rand() * 8)) % size;
}

我这样播种兰特:

unsigned long seed = time(NULL);
srand(seed);

我将种子连同我的数据作为包含校验和和长度的 header 的一部分。

我遇到的问题是,在解码时,当我再次使用从数据解码的种子为 rand 函数播种时,rand() 往往会产生像这样的最轻微的变化:

Index at encode:                  | Index at decode:
---------------------------------------------------------------------
.
.
.
At:                        142568 | At:                        142568
At:                        155560 | At:                        155552
                               --                                  --
At:                        168184 | At:                        168184
.
.
.

弄乱我的解码数据。

这是 rand() 函数的限制吗? 我 100% 确定种子被正确解码 bit-for-bit 因为我已经验证了这一点。

没有。由于多种原因,使用 rand(未完全指定并且本质上使用全局状态)的可重复性很糟糕:

  1. 你可能会使用不同的compiler/system,这可能会使用不同的RNG,

  2. 您可能使用相同的编译器,但更新到使用不同 RNG 的新版本,

  3. 您可能使用相同的编译器,相同的版本,但更新后的 libc,使用不同的 RNG,

  4. 您使用相同的编译器和库版本,但有任何其他非确定性的RNG调用顺序,包括但不限于:

    a) 其他一些随机性来源,

    b) 用户输入,

    c) 从 运行 到 运行 或

    的并发重新排序

    d) 您使用的任何库中的任何上述内容。

C 2018 7.22.2.2 2 说:

The srand function uses the argument as a seed for a new sequence of pseudo-random numbers to be returned by subsequent calls to rand. If srand is then called with the same seed value, the sequence of pseudo-random numbers shall be repeated.

这并没有明确表示程序的不同执行顺序是相同的,但我认为这是理解的。但是,它不会扩展到不同的 C 实现,包括那些链接到不同版本的 C 标准库的实现。

OpenBSD's implementation of rand 忽略对 srand 的所有调用,而是提供自动播种的加密强随机数,就像您使用 arc4random 一样;这被记录为故意偏离 C 标准。这表明,即使您的文件总是在生成它们的同一台计算机上解码,也不能保证 rand 会执行您想要的操作。

如果您需要可重复的伪随机数序列,您应该执行工业级统计软件所做的工作,即发布您自己的 PRNG 并记录您选择的算法。 (例如参见 [​​=16=]。)

此外,由于这是一个隐写术应用程序,您需要使用加密强度高 PRNG(也称为流密码) ;仅仅像 Mersenne Twister 这样统计上强大的 PRNG 还不够好。