c++ - 为什么在 main 之前的函数中使用 rand/srand 时得到相同的数字?
问题描述
为什么每次我在 main 中运行我的代码时都会得到一个不同的随机数,但是当我在函数中运行它时我得到一个静态数?
(1) Main中的随机数...
int main()
{
srand(time(0));
int loot = 1+(rand()%9);
cout << loot;
return 0;
}
(2) 主函数之外的函数中的随机数...
using namespace std;
void lootTable(){
srand(time(0));
int loot = 1+(rand()%9);
cout << loot;
}
int main()
{
cout << lootTable;
return 0;
}
解决方案
马上,您应该知道该行在std::cout << lootTable;
原始代码中的作用。它相当于std::cout << <address of lootTable function>;
,它是一个非零值,它被转换为一个布尔值,然后导致打印1
。编译时,强烈建议启用警告,至少-Wall -Wextra
. 您会立即收到有关此问题的警告。
要调用函数,您必须始终使用参数列表 ,()
即使它是空的。
srand()
只能在程序中调用一次。它为您的 PRNG(伪随机数生成器)播种,我们只播种一次 PRNG。最简单的方法是在开头main()
。
#include <cstdlib>
#include <ctime>
#include <iostream>
int lootTable() { return 1 + std::rand() % 9; }
int main() {
std::srand(std::time(0));
for (int i = 0; i < 10; ++i) {
std::cout << lootTable() << ' ';
}
std::cout << '\n';
}
两次单独运行的输出:
~/tmp
❯ ./a.out
5 5 3 8 1 2 1 9 8 8
~/tmp
❯ ./a.out
6 5 5 7 8 7 5 6 8 4
让我们看看looTable()
。我使用了您尝试回答您自己问题的签名。让我们一块一块地看。
int lootTable()
这个很简单。第一部分,int
,表示该函数返回一个整数。所以你的函数必须至少有一个return
语句。您的初始版本具有void
返回类型,这意味着该函数不会返回值。对于您的预期用途,这可能不正确。
然后,在您尝试的答案中,您修复了返回类型,但实际上没有返回任何内容,因此投反对票。
第二部分是名称,您似乎已经记下了,调用该函数没有问题。
第三部分是参数列表,在您的情况下为空。同样,这里似乎没有问题。
您应该做的而不是使用srand()
/rand()
正在使用<random>
. 它的随机性不太确定,它将 PRNG 与您想要的分布类型分开。下面的代码使用 a std::uniform_int_distribution
,这意味着每个值都有相同的出现机会。对于战利品表,如果您有不同类别的战利品,您可能更喜欢漂浮在船上的物品std::normal_distribution
或其他物品。std::lognormal_distribution
下面是一个简短的示例程序:
#include <iostream>
#include <random>
int lootTable() {
static std::mt19937 prng(std::random_device{}());
static std::uniform_int_distribution<int> values(1, 9);
return values(prng);
}
int main() {
for (int i = 0; i < 10; ++i) {
std::cout << lootTable() << ' ';
}
std::cout << '\n';
}
两次单独运行的输出:
~/tmp
❯ ./a.out
2 6 7 8 2 1 8 7 8 5
~/tmp
❯ ./a.out
9 5 7 8 4 5 8 8 1 7
该static
关键字允许 PRNG 和分布对象在函数调用之间保持不变。换句话说,它们不会在每次调用函数时都重新创建。这使他们能够在您的程序期间表现最佳。
SLIGHT TANGENT:
使用单个 32 位值
播种std::mt19937
不太理想,但对于小程序来说已经足够了。正确的播种需要足够的额外工作,std::mt19937
用一个类包装以确保完全播种是理想的。但是考虑到显示<random>
已经离主题太远了,我不会演示任何代码。但如果你好奇,这里有一个链接:https ://kristerw.blogspot.com/2017/05/seeding-stdmt19937-random-number-engine.html
推荐阅读
- postgresql - 在完成命令 postgresql 之前关闭客户端
- android - 使用来自 Activity 的上下文从适配器类中的 Viewholder 创建对象 - 没有为“上下文”传递值
- html - 如何从 Chart.js 更改 Angular 7/8 上的工具提示
- sql - 数据库设计问题解决此问题的最佳方法
- javascript - 如何使用 javascript 永久更改我网站中的 css 文件?
- sql - 如何编写查询以产生所需的结果?
- jekyll - 如何使用 jekyll-archive 获取 Jekyll 类别的博客列表
- java - 如何从文本文档中提取信息然后打印出来
- ruby - 在 ruby 中将文件名作为参数传递
- python - 如何处理 Django 中的错误