生成没有 time.h 的随机值

Ang*_*yan 1 c random srand

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

\n\n
srand(getpid()); \n
Run Code Online (Sandbox Code Playgroud)\n\n

但这似乎对我不起作用 getpid 尚未声明。这是因为我缺少它的图书馆吗?如果是的话,我需要弄清楚如何随机生成数字,而不使用除我当前拥有的库之外的任何其他库。

\n\n
#include <stdio.h>\n#include <stdlib.h>\n\n\nint main(void) {\n    int minute, hour, day, month, year;\n    srand(getpid());\n    minute = rand() % (59 + 1 - 0) + 0;\n    hour = rand() % (23 + 1 - 0) + 0;\n    day = rand() % (31 + 1 - 1) + 1;\n    month = rand() % (12 + 1 - 1) + 1;\n    year = 2018;\n\n    printf("Transferred successfully at %02d:%02d on %02d/%02d/%d\\n", hour, \n    minute, day, month, year);\n\n    return 0;\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

注意: 我只能使用库<stdio.h>和\ <stdlib.h>xe2 <string.h>\x80\x94 严格的作业准则。

\n

Nom*_*mal 5

getpid 尚未声明。

不,因为您没有在<unistd.h>声明它的地方包含标头(并且根据您的注释,您不能使用它,因为您只能使用<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;
}
Run Code Online (Sandbox Code Playgroud)

以及简单地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;
}
Run Code Online (Sandbox Code Playgroud)

和字符设备适用于 Linux、FreeBSD、macOS、iOS、Solaris、NetBSD、Tru64 Unix 5.1B、AIX 5.2、HP-UX 11i v2 以及/dev/urandomOpenBSD 5.1及更高版本。/dev/random/dev/random/dev/arandom

与往常一样,Windows 似乎不提供任何此类随机源:Windows C 程序必须使用专有的 Microsoft 接口。

randomize_helper()如果输入流为 NULL,或者无法unsigned int从中读取,则返回非零值。如果它可以从中读取无符号整数,则它用于为您可以使用访问的标准伪随机数生成器提供种子rand()(返回int0 和 之间的值RAND_MAX,包括 0 和 )。在所有情况下,randomize_helper()关闭非 NULL 流。

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

如果randomize()返回 0,rand()则应返回伪随机数。否则,rand()将返回相同的默认伪随机数序列。(它们仍然是“随机”的,但每次运行程序时都会出现相同的序列。如果randomize()返回 0,则每次运行程序时序列都会不同。)


大多数标准 Crand()实现都是线性同余伪随机数生成器,通常参数选择不佳,因此速度很慢,而且不是很“随机”。

对于非加密工作,我喜欢实现Xorshift系列函数之一,最初由 George Marsaglia 编写。它们非常非常快,并且相当随机;他们通过了大多数统计随机性测试,例如顽固测试

在 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;
}
Run Code Online (Sandbox Code Playgroud)

上面对应的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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,它PRNG_MAX有双重目的。一方面,它告诉prng()可以返回的最大值——这是一个unsigned int,而不是像 那样的 int rand()。另一方面,因为它必须是 2 32 -1 = 4294967295,所以我们也用它来确保生成序列中的下一个伪随机数时的临时结果保持为 32 位。如果uint32_tstdint.h或中声明的类型inttypes.h可用,我们可以使用它并删除掩码 ( & PRNG_MAX)。

请注意,该prng_randomize_from()函数的编写方式使其仍然有效,即使随机源无法一次提供所有请求的字节并返回“短计数”。这种情况在实践中是否会发生还有待商榷,但我更愿意确定。另请注意,如果该状态全为零,则它不接受该状态,因为这是 Xorwow PRNG 的一个禁止的初始种子状态。

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

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

  • BSD(macOS 和 Mac OS X)都有 `/dev/random` 和 `/dev/urandom`,后者记录为:_`/dev/urandom` 是对 Linux 的兼容性认可。在 Linux 上,如果熵池耗尽,“/dev/urandom”将产生较低质量的输出,而“/dev/random”将更喜欢阻塞并等待收集额外的熵。对于 Yarrow,这种选择和区分是不必要的,并且两种设备的行为相同。您可以使用其中之一。_ (2认同)