首页 > 解决方案 > 为什么在 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;
}

标签: c++randomsrand

解决方案


马上,您应该知道该行在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


推荐阅读