C++随机数生成

5
例如,以下表达式: r = (rand() % 10)+1; 生成一个从1-10的随机数。
我们如何使它生成0-10的随机数?
谢谢。

1
理解您之前问题的答案将会回答这个问题。 - Fred Nurk
5个回答

16

你离成功就差一步了!rand()函数返回一个相当大的范围内(0到RAND_MAX)的随机值。使用模运算符将其限制在较小的范围内(从 0 到 9,因为模数是 10),然后再加上 1 将其转换为从 1 到 10 的范围。

要获取 0 到 10 之间的值,可以使用 rand 函数并对其值进行 11 取模:

r = rand() % 11;

更普遍地,为了在范围[0,n]内获得随机值,您可以编写以下代码

r = rand() % (n + 1);

最后,若要获取范围在 [k, n + k] 内的值,你可以写成:

r = rand() % (n + 1) + k;

当然,正如纯粹主义者所指出的那样,这并不能确保为您提供真正均匀的值,因为使用rand()取模某个值将不能均匀分布所有整数。这通常不是一个问题(您可能只会有很小的偏差),但如果是,您可能需要考虑寻找比rand()更强大的随机数生成器。


1
你犯了一个错误,因为在文本中提到了半开区间,但在代码中生成了闭区间。 - CodesInChaos
@CodeInChaos- 哎呀!我的错。:-)我会立即修复它。 - templatetypedef
通常 rand() 最严重的问题是单个随机变量的非均匀分布,以及随机变量之间的相关性。 - CodesInChaos

11
你需要从0开始计算,不要加上+1。由于你需要11个不同的值,所以需要对11取模,而不是10。
r = rand() % 11;

请注意,您需要种子一个PRNG,以便它不总是产生相同的序列。
标准的rand()函数很糟糕。随机数的质量非常低,不适用于许多目的。我强烈建议使用更好的生成器。 Mersenne twister是一个受欢迎的选择,在我的一个项目中,我使用了Well512,因为它快速、好用且易于实现。
如果用户不能预测随机数,即使使用好的PRNG也不够,您必须选择加密PRNG。除非您知道种子,否则它们既不能被预测(具有现实计算能力),也不能与真正的随机数区分开来。它们稍微慢一些。

实际上,它适用于绝大多数情况。玩电脑游戏的人数远远超过地球上统计学家的数量 :-) 但无论如何,对于正确的答案+1。 - paxdiablo
我曾看到它在简单的模拟中崩溃,比如随机游走。 - CodesInChaos
有时候甚至需要使用加密强度的 PRNG 来设计电脑游戏。你肯定不想让在线角色扮演游戏中的物品掉落 PRNG 可以被预测。 - CodesInChaos
实际上这并不重要。客户端的差异足以使其下降。更正式地说:如果您对适当的伪随机数生成器的输出进行真正的随机抽样,则结果将是随机的。(适当的 PRNG 会淘汰 http://xkcd.com/221/ 这样的东西) - MSalters

5

C++11中,使用std::uniform_real_distribution生成均匀分布要简单得多,而且出错概率更低。对于整数情况,可以使用std::uniform_int_distribution。下面是一个使用std::uniform_real_distribution的示例,它显示了一个简单的图形,以粗略地演示它是均匀的:

#include <iostream>
#include <iomanip>
#include <string>
#include <random>

int main()
{
    std::random_device rd;

    //
    // Engines 
    //
    std::mt19937 e2(rd());
    //std::knuth_b e2(rd());
    //std::default_random_engine e2(rd()) ;

    std::uniform_real_distribution<> dist(0, 10);

    const int nstars=95;     // maximum number of stars to distribute
    const int nintervals=10; // number of intervals

    int p[nintervals]={};

    for (int i=0; i<100000; ++i)
    {
      double number = dist(e2);
      ++p[int(number)];
    }

    std::cout << std::fixed; std::cout.precision(1);

    for (int i=0; i<nintervals; ++i)
    {
      std::cout << float(i) << "-" << std::setw(4) << float(i+1) << ": ";
      std::cout << std::string(p[i]*nstars/100000,'*') << std::endl;
    }

    return 0 ;
}

样例结果:

0.0- 1.0: *********
1.0- 2.0: *********
2.0- 3.0: *********
3.0- 4.0: *********
4.0- 5.0: *********
5.0- 6.0: *********
6.0- 7.0: *********
7.0- 8.0: *********
8.0- 9.0: *********
9.0-10.0: *********

有些示例代码取自于这个参考文献


1
这是使用现代C++生成随机数的更简单方法。
#include <iostream>
#include <random>

int main()
{
    std::random_device device;
    std::default_random_engine engine(device());
    std::uniform_int_distribution<int> r(0, 10);
    std::cout << r(engine) << std::endl;
}

为什么这个解决方案比(rand() % 11)更好?除了您可以选择不同的引擎和来源之外。您应该在答案中添加更多上下文。 - HSchmale
每次需要随机数时声明一个新的std::uniform_distribution是否安全?我想将其简化为一个简单的random(min, max)函数,而不是在需要随机数的每个地方都复制粘贴所有这些代码。我不想为每个可能的min和max组合保留uniform_int_distribution对象。 - Throw Away Account

0
这是一种生成随机数的基本方法。使用 static_cast 的代码行根据当前时间对随机数生成器进行了种子化,这意味着您可以获得更多的数字,而不仅仅是 0 到 10 之间的一个数字。
#include <iostream>
#include <ctime>
#include <cstdlib>

using namespace std;

int main()
{
    srand(static_cast<unsigned int>(time(0))); // Seed random number generator
    unsigned int r = rand() % 10; // Generate random number
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接