我正在尝试用骰子制作游戏,并且我需要在其中包含随机数(以模拟骰子的侧面。我知道如何使其介于 1 和 6 之间)。使用
#include <cstdlib>
#include <ctime>
#include <iostream>
using namespace std;
int main()
{
srand((unsigned)time(0));
int i;
i = (rand()%6)+1;
cout << i << "\n";
}
不能很好地工作,因为当我运行程序几次时,我得到的输出如下:
6
1
1
1
1
1
2
2
2
2
5
2
所以我想要一个每次都会生成 不同 随机数的命令,而不是连续 5 次相同的随机数。有没有命令可以做到这一点?
原文由 Predictability 发布,翻译遵循 CC BY-SA 4.0 许可协议
您的测试应用程序最根本的问题是您调用
srand
一次,然后调用rand
一次并退出。srand
函数的重点是用随机种子初始化 伪随机数序列。这意味着如果您在两个不同的应用程序 中将相同的值 传递给
srand
(具有相同的srand
/rand
实现),那么 您将得到完全相同的序列rand()
在这两个应用程序之后读取的值。但是,在您的示例应用程序中,伪随机序列仅包含一个元素 - 从种子生成的伪随机序列的第一个元素等于
1 sec
精度的当前时间。你期望在输出上看到什么?显然,当您碰巧在同一秒运行应用程序时-您使用相同的种子值-因此您的结果当然是相同的(正如 Martin York 在对该问题的评论中已经提到的那样)。
实际上,您应该调用
srand(seed)
一次,然后 多次 调用rand()
并分析该序列 - 它应该看起来是随机的。修正 1 - 示例代码:
好的我明白了。显然口头描述是不够的(可能是语言障碍之类的…… :))。
基于问题中使用的相同
srand()/rand()/time()
函数的老式 C 代码示例:^^^ 单次运行的程序应该看起来是随机的。
请注意,我不建议在生产中使用
rand
/srand
函数,原因如下,我绝对不建议使用函数time
作为一个随机种子,因为 IMO 已经很明显了。这些对于教育目的很好,有时也可以说明这一点,但对于任何严肃的用途,它们大多是无用的。修正案 2 - 详细说明:
重要的是要理解,到目前为止,还 没有 C 或 C++ 标准特性(库函数或类)最终确定地产生实际随机数据(即由标准保证实际上是随机的)。解决这个问题的唯一标准特性是 std::random_device ,遗憾的是它仍然不能保证实际随机性。
根据应用程序的性质,您应该首先确定您是否真的需要真正随机(不可预测的)数据。 当你确实需要真正的随机性时, 值得注意的情况是信息安全——例如生成对称密钥、非对称私钥、盐值、安全令牌等。
然而,安全级随机数是一个独立的行业,值得单独写一篇文章。我在我的 这个答案 中简要讨论了它们。
在大多数情况下 ,伪随机数生成器 就足够了——例如用于科学模拟或游戏。在某些情况下,甚至需要一致定义的伪随机序列——例如,在游戏中,您可以选择在运行时生成完全相同的地图,以避免在安装包中存储大量数据。
最初的问题和重复出现的大量相同/相似的问题(甚至许多被误导的“答案”)表明,首先重要的是区分随机数和伪随机数,并了解什么是伪随机数序列首先并要意识到伪随机数生成器的使用方式与使用真随机数生成器的方式不同。
^^^ 这种直观的期望在涉及 伪随机数生成器 的所有情况下都是 非常错误和有害 的——尽管对于真正的随机数是合理的。
虽然存在“随机数”的有意义的概念(有点) - 没有“伪随机数”这样的东西。 伪随机数生成器 实际上产生伪随机数 序列。
伪随机序列实际上总是 确定性 的(由它的算法和初始参数决定)——也就是说,它实际上没有任何随机性。
当专家谈论 PRNG 的质量时,他们实际上谈论的是生成序列(及其显着的子序列)的统计特性。例如,如果您通过轮流使用它们来组合两个高质量的 PRNG - 您可能会产生不好的结果序列 - 尽管它们分别生成好的序列(这两个好的序列可能只是相互关联,因此组合不好)。
具体来说
rand()
/srand(s)
函数对提供了一个单一的每进程非线程安全(!)伪随机数序列,使用实现定义的算法生成。函数rand()
产生范围内的值[0, RAND_MAX]
。引用 C11 标准 (ISO/IEC 9899:2011):
许多人合理地期望
rand()
会在0
到RAND_MAX
范围内产生一系列半独立均匀分布的数字。好吧,它当然应该(否则它没用),但不幸的是,不仅标准不需要这样做 - 甚至还有明确的免责声明指出 “无法保证产生的随机序列的质量” 。在某些历史案例rand
/srand
实施的质量确实很差。即使在现代实现中它很可能已经足够好 - 但信任被打破并且不容易恢复。除了它的非线程安全特性之外,它在多线程应用程序中的安全使用也变得棘手和有限(仍然可能——您可以只从一个专用线程中使用它们)。新类模板 std::mersenne_twister_engine<> (及其便利的 typedefs -
std::mt19937
/std::mt19937_64
具有良好的模板参数组合)提供在 C++ 中定义的 每个对象的 伪随机数生成器11个标准。使用相同的模板参数和相同的初始化参数,不同的对象将在使用符合 C++11 标准库的任何应用程序中的任何计算机上生成完全相同的每个对象输出序列。此类的优势在于其可预测的高质量输出序列和跨实现的完全一致性。在 C++11 标准中定义了更多的 PRNG 引擎 - std::linear_congruential_engine<> (历史上用作公平质量
srand/rand
在一些 C 标准库实现中的算法)和 std::subtract_with_carry_engine<> 。它们还生成完全定义的参数相关的每个对象输出序列。上面过时的 C 代码的现代 C++11 示例替换:
使用 std::uniform_int_distribution<> 的先前代码的版本