我正在尝试在C中实现一组编码和解码隐写功能,其中我使用rand()将数据随机分散到数组中。
我使用rand计算随机索引,如下所示:
unsigned int getRandIndex(int size) {
return ((unsigned)(rand() * 8)) % size;
}
我像这样播种:
unsigned long seed = time(NULL);
srand(seed);
我将种子和我的数据一起作为包含校验和和长度的报头的一部分。
我遇到的问题是,在解码时,当我用从数据中解码的种子再次播种rand函数时,rand()倾向于产生最轻微的变化,如:
Index at encode: | Index at decode:
---------------------------------------------------------------------
.
.
.
At: 142568 | At: 142568
At: 155560 | At: 155552
-- --
At: 168184 | At: 168184
.
.
.
弄乱了我的解码数据。
这是rand()函数的限制吗?我100%确定种子被正确地逐位解码,因为我已经验证了这一点。
C 2018 7.22.2.2 2说:
srand
函数使用该参数作为一个新的伪随机数序列的种子,这些伪随机数序列将在随后调用rand
时返回。如果再用相同的种子值调用srand
,则重复伪随机数序列。
这并没有明确地说序列在程序的不同执行中是相同的,但我认为这是可以理解的。但是,它不能扩展到不同的C实现,包括那些由于链接到不同版本的C标准库而产生的实现。
OpenBSD的rand
实现忽略对srand
的所有调用,而是提供自动种子,加密强随机数,就像您使用arc4random
一样;这被记录为对C标准的故意偏离。这表明,即使您的文件总是在生成它们的同一台计算机上解码,也不能保证rand
会做您想要的。
如果您需要可重复的伪随机数序列,您应该做工业级统计软件所做的事情,即发布您自己的PRNG并记录您选择的算法。(参见r的?Random
帮助页面)
No。为了再现性,使用rand
(没有精确指定,并且固有地使用全局状态)是很糟糕的,原因有很多:
-
你可能会使用不同的编译器/系统,它可能会使用不同的RNG,
-
你可能会使用相同的编译器,但更新到一个新版本,使用不同的RNG,
-
你可能会使用相同的编译器,相同的版本,但有一个更新的
libc
,使用不同的RNG, -
您使用相同的编译器和库版本,但有任何其他不确定的RNG调用顺序,包括但不限于:
a)其他随机性来源,
b)用户输入, c)从运行到运行的并发重新排序,或者d)在您使用的任何库中执行上述任何操作。