getpid 尚未声明。
不,因为您没有在声明它的地方包含<unistd.h> 标头(并且根据您的comment,您不能使用它,因为您只能使用<stdlib.h>、<string.h> 和<stdio.h>)。
在这种情况下,我会使用类似的东西
#include <stdlib.h>
#include <stdio.h>
static int randomize_helper(FILE *in)
{
unsigned int seed;
if (!in)
return -1;
if (fread(&seed, sizeof seed, 1, in) == 1) {
fclose(in);
srand(seed);
return 0;
}
fclose(in);
return -1;
}
static int randomize(void)
{
if (!randomize_helper(fopen("/dev/urandom", "r")))
return 0;
if (!randomize_helper(fopen("/dev/arandom", "r")))
return 0;
if (!randomize_helper(fopen("/dev/random", "r")))
return 0;
/* Other randomness sources (binary format)? */
/* No randomness sources found. */
return -1;
}
还有一个简单的main() 来输出一些伪随机数:
int main(void)
{
int i;
if (randomize())
fprintf(stderr, "Warning: Could not find any sources for randomness.\n");
for (i = 0; i < 10; i++)
printf("%d\n", rand());
return EXIT_SUCCESS;
}
/dev/urandom 和 /dev/random 字符设备可用于 Linux、FreeBSD、macOS、iOS、Solaris、NetBSD、Tru64 Unix 5.1B、AIX 5.2、HP-UX 11i v2 和 /dev/random 和 /dev/arandom在 OpenBSD 5.1 及更高版本上。
像往常一样,Windows 似乎不提供任何此类随机源:Windows C 程序必须改用专有的 Microsoft 接口。
如果输入流为 NULL,或者无法从中读取 unsigned int,randomize_helper() 将返回非零值。如果它可以从中读取一个无符号整数,则它用于为您可以使用rand() 访问的标准伪随机数生成器提供种子(它返回一个介于 0 和RAND_MAX 之间的int,包括在内)。在所有情况下,randomize_helper() 都会关闭非 NULL 流。
您可以轻松地将其他二进制随机源添加到randomize()。
如果randomize() 返回 0,rand() 应该返回伪随机数。否则,rand() 将返回相同的默认伪随机数序列。 (它们仍然是“随机的”,但每次运行程序时都会出现相同的序列。如果randomize()返回0,则每次运行程序时序列都会不同。)
大多数标准 C rand() 实现都是线性同余伪随机数生成器,通常参数选择不佳,因此速度较慢,而且不是很“随机”。
对于非加密工作,我喜欢实现 Xorshift 系列函数之一,最初由 George Marsaglia 编写。它们非常非常快,而且相当随机;他们通过了大多数统计随机性测试,例如diehard tests。
在 OP 的情况下,可以使用 xorwow 生成器。根据当前的 C 标准,unsigned int 至少为 32 位,因此我们可以将其用作生成器类型。让我们看看替换标准 srand()/rand() 的实现是什么样的:
#include <stdlib.h>
#include <stdio.h>
/* The Xorwow PRNG state. This must not be initialized to all zeros. */
static unsigned int prng_state[5] = { 1, 2, 3, 4, 5 };
/* The Xorwow is a 32-bit linear-feedback shift generator. */
#define PRNG_MAX 4294967295u
unsigned int prng(void)
{
unsigned int s, t;
t = prng_state[3] & PRNG_MAX;
t ^= t >> 2;
t ^= t << 1;
prng_state[3] = prng_state[2];
prng_state[2] = prng_state[1];
prng_state[1] = prng_state[0];
s = prng_state[0] & PRNG_MAX;
t ^= s;
t ^= (s << 4) & PRNG_MAX;
prng_state[0] = t;
prng_state[4] = (prng_state[4] + 362437) & PRNG_MAX;
return (t + prng_state[4]) & PRNG_MAX;
}
static int prng_randomize_from(FILE *in)
{
size_t have = 0, n;
unsigned int seed[5] = { 0, 0, 0, 0, 0 };
if (!in)
return -1;
while (have < 5) {
n = fread(seed + have, sizeof seed[0], 5 - have, in);
if (n > 0 && ((seed[0] | seed[1] | seed[2] | seed[3] | seed[4]) & PRNG_MAX) != 0) {
have += n;
} else {
fclose(in);
return -1;
}
}
fclose(in);
prng_seed[0] = seed[0] & PRNG_MAX;
prng_seed[1] = seed[1] & PRNG_MAX;
prng_seed[2] = seed[2] & PRNG_MAX;
prng_seed[3] = seed[3] & PRNG_MAX;
prng_seed[4] = seed[4] & PRNG_MAX;
/* Note: We might wish to "churn" the pseudorandom
number generator state, to call prng()
a few hundred or thousand times. For example:
for (n = 0; n < 1000; n++) prng();
This way, even if the seed has clear structure,
for example only some low bits set, we start
with a PRNG state with set and clear bits well
distributed.
*/
return 0;
}
int prng_randomize(void)
{
if (!prng_randomize_from(fopen("/dev/urandom", "r")))
return 0;
if (!prng_randomize_from(fopen("/dev/arandom", "r")))
return 0;
if (!prng_randomize_from(fopen("/dev/random", "r")))
return 0;
/* Other sources? */
/* No randomness sources found. */
return -1;
}
上面对应的main()是
int main(void)
{
int i;
if (prng_randomize())
fprintf(stderr, "Warning: No randomness sources found!\n");
for (i = 0; i < 10; i++)
printf("%u\n", prng());
return EXIT_SUCCESS;
}
请注意,PRNG_MAX 有双重用途。一方面,它告诉prng() 可以返回的最大值——这是一个unsigned int,而不是像rand() 这样的int。另一方面,因为它必须是 232-1 = 4294967295,所以我们也用它来保证序列中生成下一个伪随机数时的临时结果保持 32 位。如果在 stdint.h 或 inttypes.h 中声明的 uint32_t 类型可用,我们可以使用它并删除掩码 (& PRNG_MAX)。
请注意,prng_randomize_from() 函数的编写使其仍然有效,即使随机源无法一次提供所有请求的字节,并返回 “短计数”。这在实践中是否会发生还有待商榷,但我更愿意确定。另请注意,如果该状态全为零,则它不接受该状态,因为这是 Xorwow PRNG 的一个被禁止的初始种子状态。
您显然可以在同一个程序中同时使用srand()/rand() 和prng()/prng_randomize()。我编写它们是为了让 Xorwow 生成器函数都以 prng 开头。
通常,我确实将 PRNG 实现放入头文件中,这样我就可以通过编写一个小测试程序轻松地对其进行测试(以验证它是否有效);而且我也可以通过切换到另一个头文件来切换 PRNG 实现。 (在某些情况下,我将 PRNG 状态放入一个结构中,并让调用者提供指向该状态的指针,以便可以同时使用任意数量的 PRNG,彼此独立。)