【问题标题】:How to generate either 0 or 1 randomly in C如何在C中随机生成0或1
【发布时间】:2016-10-03 19:46:52
【问题描述】:

我已经阅读了很多关于这个主题的帖子:

How does rand() work? Does it have certain tendencies? Is there something better to use?

How does the random number generator work in C

这就是我得到的:

1) xn+1 取决于 xn,即先前生成的随机数。

2) 不建议在程序中多次初始化种子。

3) 使用rand()%2 随机生成 0 或 1 是一种不好的做法。

我的问题是:

1) 是否有任何其他库我错过了查看以生成完全随机数(0 或 1)而不依赖于先前的输出?

2) 如果有任何其他解决方法可以使用内置的rand() 函数来满足要求?

3) 在程序中多次初始化种子有什么副作用?

代码sn-p:

srand(time(NULL));
d1=rand()%2;
d2=rand()%2;

这里我的意图是让 d1 和 d2 完全相互独立。

我最初的想法是这样做:

srand(time(NULL));
d1=rand()%2;
srand(time(NULL));
d2=rand()%2;

但正如我之前提到的基于其他帖子的那样,我认为这是一种不好的做法?

那么,有人可以回答上述问题吗?如果我完全遗漏了一件显而易见的事情,我深表歉意。

【问题讨论】:

  • 太宽泛了,问题太多了!你知道随机数和从伪随机数生成器获得的数字之间的区别吗? (提示:使用 PRNG:每个数字 x_i 都完全依赖于之前的数字。当使用一些熵源(如时间)重新播种时,这种情况会发生变化;但这非常复杂,大多数人不理解后果)
  • 简短的回答是在程序开始时只调用一次srand(time(NULL)),然后使用rand() / N 生成您的号码。我们选择N = ceil((RAND_MAX + 1) / 2),但是为了避免RAND_MAX == INT_MAX时溢出,我们用代码int N = RAND_MAX / 2 + 1计算。
  • @Dietrich Epp 您的 cmets 看起来很有趣,但看不出 rand() / Nrand() % 2 的优势。甚至可以使用rand() & 1 来避免div/mod 代码。
  • @chux: rand() % 2 在编译时不会发出任何 div 或 mod 指令。但是,rand() & 1 之所以不好,是因为许多随机数生成器的低位输出很差。线性同余生成器仍然并不少见,用 Wikipedia 的话来说,“当 m 是 2 的幂时,LCG 的低位不应该被依赖于任何程度的随机性。”

标签: c random


【解决方案1】:
  1. 是否有任何其他库我错过了查看以生成介于 0 和 1 之间的完全随机数而不依赖于先前输出的库?

不在标准 C 库中。还有许多其他库可以生成“更好”的伪随机数。

  1. 如果有任何其他解决方法可以使用内置的 rand() 函数来满足要求?

rand 的大多数标准库实现都会生成随机数序列,其中低位具有短序列和/或彼此之间的独立性不像人们希望的那样。高阶位通常分布得更好。所以使用标准库rand函数生成随机单个位(0或1)的更好方法是:

(rand() > RAND_MAX / 2)

或使用内部位:

(rand() & 0x400U != 0)

这些将产生与大多数标准库rand 实现相当不相关的序列,并且不会比检查低位位产生更多的计算开销。如果这对您来说还不够好,您可能需要研究其他伪随机数生成器。

所有这些(包括rand() % 2)都假设RAND_MAX 是奇数,这几乎总是如此。 (如果RAND_MAX 是偶数,那么可能的值是奇数,并且任何将奇数的可能值分成两个阵营的方法都必须稍有偏差。)

  1. 在程序中多次初始化种子有什么副作用?

您应该将随机数生成器视为在播种后生成“不是非常随机”的数字,随着您连续生成新的随机数,质量会提高。请记住,如果您使用某个种子为随机数生成器播种,您将获得完全相同的序列,就像下次使用相同种子为生成器播种时一样。 (由于time() 返回一个秒数,连续两次快速连续调用通常会产生完全相同的数字,或者偶尔会产生两个连续的数字。但绝对不是两个随机不相关的数字。)

因此,重新播种的副作用是您获得的随机数更少,并且可能与您上次重新播种时获得的随机数完全相同。

【讨论】:

  • 为什么“应该认为随机数生成器在播种后产生“不是非常随机”的数字”如果这是一些随机数生成器的问题?鉴于rand() 的定义如此微弱,这当然不是特定的行为。
  • @chux 我认为这来自seeding from time-idea。重新播种时,根据时间不同,基于这个时间的不同种子并没有那么不同。对于某些 PRNG(例如 Mersenne Twister),众所周知,相似的种子需要很长时间才能发散/其他观点:很难播种多个 MT,因此这些输出之间没有相关性。
  • @chux:基本上是 sascha 所说的。大多数 RNG 从相似的种子中缓慢分化。当然,您可以从随机数中播种,但鸡和蛋:-)。当然没有具体说明;低位的非独立性也不是。但是避免重新播种和不使用低位都是恕我直言的好建议。
  • 同意上述观点,除了“大多数 RNG 与相似种子的分歧缓慢”。我熟悉 MT 的弱点,但知道适用于大多数 PRNG 的证据。
  • @chux, ok, most prngs 言过其实。但对于仍然常用的 LCG 和 MT 来说,情况确实如此。无论如何,我所建议的只是防御性启发式,而不是绝对规则。
【解决方案2】:

1) 还有其他我想看的库吗 生成一个介于 0 和 1 之间的完全随机数,而不依赖于 之前的输出?

这个子问题与 Stack Overflow 无关,但我会指出,POSIX 和 BSD 系统有一个名为 random() 的替代随机数生成器函数,如果你正在为这样的平台编程,你可以考虑使用它(例如 Linux、OS X)。

2) 如果还有其他使用内置 rand() 函数的解决方法 满足要求?

传统计算机(相对于量子计算机)是确定性机器。他们无法做到真正的随机性。每个完全程序化的“随机数生成器”实际上都是一个 psuedo-随机数生成器。它们生成完全确定的序列,但是来自给定调用集的值以与目标概率分布(通常是均匀分布)大致一致的方式分布在生成器的范围内。

某些操作系统支持生成数字,这些数字依赖于比计算序列更混乱和更不可预测的事物。例如,他们可能会从鼠标移动、CPU 温度变化或其他此类来源收集信息,以产生更客观的随机(但仍然是确定性的)数字。例如,Linux 就有这样一个驱动程序,它经常作为特殊文件/dev/random 公开。这些问题在于它们的熵存储有限,因此无法以持续的高速率提供数字。但是,如果您只需要几个随机数,那么这可能是一个合适的来源。

3) 多次初始化种子的副作用是什么 一个程序?

代码sn-p:

srand(time(NULL));
d1=rand()%2;
d2=rand()%2;

我的目的是让 d1 和 d2 完全独立 其他。

我最初的想法是这样做:

srand(time(NULL));
d1=rand()%2;
srand(time(NULL));
d2=rand()%2;

但正如我之前提到的,这是基于其他帖子的,这是一个 我想这是不好的做法?

如果您希望 d1d2 有 50% 的概率不同,那确实很糟糕。 time() 返回自 epoch 以来的 数,因此当两次如此接近地调用时,它很可能会返回相同的值。伪随机数的序列完全由种子决定(这是一个特性,而不是错误),当你为 PRNG 播种时,你会重新启动序列。即使您使用更高分辨率的时钟来使种子更有可能发生差异,您也不能以这种方式逃避相关性;您只需为您更改函数生成数字。并且结果对输出分布没有相同的保证。

此外,当您执行rand() % 2 时,您只使用它为您生成的大约 log2(RAND_MAX) + 1 位中的一位。在整个 PRNG 周期内,您可以预期该位对每个值取相同的次数,但在较窄的范围内,您有时可能会看到一些相关性。

最后,您要求两个随机数完全相互独立可能有点过分了。通常,一次调用的伪随机结果与先前调用的结果没有明显相关性就足够了。即使每次调用只使用一个位,您的第一个代码 sn-p 也可能很好地实现了这一点。但是,如果您更喜欢使用更多位,那么您可以谨慎地将您选择的数字基于rand() 返回的值中设置的位数的奇偶校验。

【讨论】:

  • 也许 log2(RAND_MAX + 1.0) 而不是 log2(RAND_MAX) + 1 来计算一个不错的整数答案?
  • @chux,我选择了我所做的公式,因为它不预设 RAND_MAX 小于 2 的幂,但我确实默默地假设结果会被截断为整数。您会发现仍有一些值偏离 1。我没有详细介绍答案中的所有细节,而是对其进行了更新,以说明位数是近似的。
【解决方案3】:

使用这个

(double)rand() / (double)RAND_MAX

完全随机数.....不依赖于先前的输出?

实际上计算机无法生成完全随机数。必须有一些依赖关系。但对于几乎所有实际用途,您都可以使用rand()

多次初始化种子的副作用

没有副作用。但这意味着你完全否定了使用 rand() 的意义。如果您每次都重新初始化种子,随机数更多地取决于时间(和处理器)。

使用内置 rand() 函数的任何其他解决方法

你可以这样写:

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

int main(int argc,char *argv[])
{
    srand(time(NULL));
    printf("%lf\n",(double)rand()/(double)RAND_MAX);
    printf("%lf\n",(double)rand()/(double)RAND_MAX);
}

如果你想生成 0 或 1,我认为使用 rand()%2 非常好,因为偶数的概率与奇数的概率相同(所有概率对于无偏随机数生成器而言,numbers 是相等的)。

【讨论】:

  • 详细信息:这将不时生成 0.0 和 1.0。 OP 的目标在终点方面尚不清楚。 / (double)RAND_MAX 不精确,也会创建小工件 - 但这可能符合 OP 的目标。
  • 虽然 OP 说 0 和 1 之间,但他的代码说他希望生成的每个数字 要么 零或一。
  • @JohnBollinger 好的。为零或一添加了答案
猜你喜欢
  • 2014-09-22
  • 1970-01-01
  • 1970-01-01
  • 2019-02-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-21
相关资源
最近更新 更多