C++ 中的种子随机数生成器
C++ 等编程语言不会生成真正的随机数。相反,计算机使用数学函数生成伪随机数。
本文讨论了生成随机数中种子的概念以及为 C++ 中用于生成随机数的函数提供种子的方法。
C++ 中随机数生成器中种子的概念
C++ 使用确定性算法生成随机数序列。因此,数字序列是伪随机的,而不是纯粹的概率。
在这种情况下,种子充当算法的起点。你不应将其视为生成的第一个数字将是种子。
相反,该算法从种子定义的分布中随机选择数字。如果你为算法提供相同的种子,它将生成相同的伪随机数序列。
但是,大多数情况下,你可能需要为每次执行生成不同的伪随机数序列。在这种情况下,你需要在每次执行算法时为其提供不同的种子。
使用 srand()
函数在 C++ 中播种随机数生成器
srand()
函数接受无符号整数作为参数。它使用参数为生成伪随机数的算法播种。
语法:
void srand(unsigned int seed);
如果你提供 1
作为 srand()
函数的参数,它会将伪随机数生成器初始化为其初始值。生成器产生与上次调用 rand()
函数相同的结果。
让我们看一个示例,该示例使用从用户那里获取的任意数字作为输入来播种伪随机数生成器。
示例代码:
#include <iostream>
using namespace std;
int main() {
unsigned int seed;
cout << "Enter seed value:\n";
cin >> seed;
srand(seed);
cout << "Successfully seeded the generator\n";
return 0;
}
输出:
mohtashim@mohtashim:~/eclipse-workspace/Java2Blog$ g++ seed_example.cc
mohtashim@mohtashim:~/eclipse-workspace/Java2Blog$ ./a.out
Enter seed value:
12
Successfully seeded the generator
使用 time()
函数在 C++ 中播种随机数生成器
前一种方法的问题是用户可以多次输入相同的数字。如果你需要确保每次执行算法时都提供不同的种子,请使用 time()
函数为伪随机数生成器提供种子。
C++ 中的 time()
函数返回当前 UNIX 时间戳,即自 UTC 时间 1970 年 1 月 1 日 00:00 小时以来经过的秒数。
语法:
time_t time(time_t* timer);
该函数将参数作为 time_t
类型的指针。如果你提供对函数的非空引用作为参数,它会将 time_t
类型的对象设置为保存当前时间戳的参数。
time_t
类型是算术类型的别名,可以保存当前的 UNIX 时间戳值。它实际上是一个无符号整数值。
示例代码:
#include <iostream>
using namespace std;
int main() {
srand(time(NULL));
cout << "Successfully seeded the generator\n";
return 0;
}
请注意,代码将 NULL
作为参数传递给 time()
函数。根据代码,出于任何原因,你不需要类型为 time_t
的对象。
输出:
mohtashim@mohtashim:~/eclipse-workspace/Java2Blog$ g++ seed_example.cc
mohtashim@mohtashim:~/eclipse-workspace/Java2Blog$ ./a.out
Successfully seeded the generator
播种要避免的随机生成器错误
通常,你在代码的循环中生成一系列伪随机数。大多数人犯的一个常见错误是每次循环执行时都为生成器播种。
在这种情况下,你可能会得到重复的数字,因为每次你使用相同的数字播种时,生成器都会创建相同的数字序列。
即使你使用 time()
函数为随机生成器播种也是如此,因为 time()
函数返回自过去某个固定日期以来经过的秒数。
另一方面,循环将执行得更快,因此每次调用 time()
函数将保持返回相同的值,直到经过一秒。这样,你最终将向生成器播种相同的值。
示例代码:
#include <iostream>
using namespace std;
int main() {
for (int i = 1; i <= 10; i++) {
srand(time(NULL));
cout << rand() << " ";
}
cout << endl;
return 0;
}
输出:
mohtashim@mohtashim:~/eclipse-workspace/Java2Blog$ g++ seed_example.cc
mohtashim@mohtashim:~/eclipse-workspace/Java2Blog$ ./a.out
1524491454 1524491454 1524491454 1524491454 1524491454 1524491454 1524491454 1524491454 1524491454 1524491454
代码在循环内播种随机生成器,并且执行十次相同的结果。你应该始终在循环之前为随机数生成器播种以避免这种陷阱。
让我们看一下代码在循环外播种随机生成器的示例。
示例代码:
#include <iostream>
using namespace std;
int main() {
srand(time(NULL));
for (int i = 1; i <= 10; i++) {
cout << rand() << " ";
}
cout << endl;
return 0;
}
输出:
mohtashim@mohtashim:~/eclipse-workspace/Java2Blog$ g++ seed_example.cc
mohtashim@mohtashim:~/eclipse-workspace/Java2Blog$ ./a.out
213462937 1076978976 1207347426 8310730 1551061902 266528745 944000672 871831053 1678325834 868781842
观察代码导致不同的数字序列。
结论
你可以将种子提供给充当算法起点的伪随机生成器,但你应该小心避免陷阱,如文章中所述。否则,可能会导致不需要的结果。
你可以阅读更多关于在播种后生成伪随机数序列的 rand()
函数此处。