在没有 time.h 的情况下生成随机值

Generating random values without time.h

我想在不使用 time.h 库的情况下重复生成随机数。我看到另一个 post 关于使用

srand(getpid()); 

然而这似乎对我不起作用 getpid 尚未声明。这是因为我错过了图书馆吗?如果是的话,我需要弄清楚如何在不使用任何其他库的情况下随机生成数字。

#include <stdio.h>
#include <stdlib.h>


int main(void) {
    int minute, hour, day, month, year;
    srand(getpid());
    minute = rand() % (59 + 1 - 0) + 0;
    hour = rand() % (23 + 1 - 0) + 0;
    day = rand() % (31 + 1 - 1) + 1;
    month = rand() % (12 + 1 - 1) + 1;
    year = 2018;

    printf("Transferred successfully at %02d:%02d on %02d/%02d/%d\n", hour, 
    minute, day, month, year);

    return 0;
}

我只能使用库 <stdio.h><stdlib.h><string.h> — 作业的严格准则。

getpid hasn't been declared.

不,因为你没有在声明的地方包含 <unistd.h> header(并且根据你的 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() return 非零。如果它可以从中读取一个 unsigned int,它会被用来作为标准伪随机数生成器的种子,您可以使用 rand()(其中 return 是一个介于 0 和 [=27= 之间的 int ], 包括的)。在所有情况下,randomize_helper() 关闭 non-NULL 流。

您可以轻松地将其他二进制随机源添加到 randomize()

如果randomize() returns 0,rand()应该是return伪随机数。否则,rand() 将 return 相同的默认伪随机数序列。 (它们仍然是"random",但是每次运行程序都会出现相同的序列。如果randomize() returns 0,每次你的序列都会不同运行 程序。)


大多数标准 C rand() 实现都是线性同余伪随机数生成器,通常参数选择不当,因此速度较慢,而且不是很 "random"。

对于non-cryptographic的工作,我喜欢实现Xorshift family of functions, originally by George Marsaglia. They are very, very fast, and reasonably random; they pass most of the statistical randomness tests like the 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() 可以 return —— 这是一个 unsigned int,而不是像 rand() 这样的 int。另一方面,因为它必须是232-1 = 4294967295,所以我们也用它来保证序列中生成下一个伪随机数时的临时结果保持32位。如果在 stdint.hinttypes.h 中声明的 uint32_t 类型可用,我们可以使用它并删除掩码 (& PRNG_MAX).

请注意,prng_randomize_from() 函数的编写使其仍然有效,即使随机源不能一次提供所有请求的字节,并且 returns a "short count"。这是否会在实践中发生还有待商榷,但我更愿意确定。另请注意,它不接受全为零的状态,因为这是 Xorwow PRNG 唯一禁止的初始种子状态。

您显然可以在同一程序中同时使用 srand()/rand()prng()/prng_randomize()。我写它们是为了让 Xorwow 生成器函数都以 prng 开头。

通常,我会将 PRNG 实现放入 header 文件中,这样我就可以通过编写一个小测试程序轻松地测试它(以验证它是否有效);而且这样我就可以通过切换到另一个 header 文件来切换 PRNG 实现。 (在某些情况下,我将 PRNG 状态放入一个结构中,并让调用者提供一个指向该状态的指针,以便可以同时使用任意数量的 PRNG,彼此独立。)

however that doesn't seem to work for me getpid hasn't been declared.

那是因为您需要为 getpid() 添加 headers:

   #include <sys/types.h>
   #include <unistd.h>

另一种选择是使用 time() 播种(而不是 getpid()):

   srand((unsigned int)time(NULL));

正如其他答案所指出的,您需要包含 unistd.h header。如果您不想这样做,请将 getpid() 的声明放在 main() 之上。在此处阅读 getpid() 的手册页 http://man7.org/linux/man-pages/man2/getpid.2.html

一种方法可能是

#include <stdio.h>
#include <stdlib.h>
pid_t getpid(void); /* put the declrataion of getpid(), if don't want to include the header */
int main(void) {

   /* .. some code .. */
   return 0;
} 

或者您可以像

一样使用 time()
srand((unsigned int)time(NULL));